pax_global_header00006660000000000000000000000064131324361140014510gustar00rootroot0000000000000052 comment=73e4f777d5f1bbb7fc1cde2590e418cee427cf41 ring-1.6.2/000077500000000000000000000000001313243611400124555ustar00rootroot00000000000000ring-1.6.2/.gitignore000066400000000000000000000002561313243611400144500ustar00rootroot00000000000000lib classes ring-*.jar ring.jar ring-standalone.jar pom.xml pom.xml.asc autodoc .lein-failures codox .lein-deps-sum .lein-env target checkouts .lein-repl-history .nrepl-port ring-1.6.2/.travis.yml000066400000000000000000000000541313243611400145650ustar00rootroot00000000000000language: clojure script: lein sub test-all ring-1.6.2/CHANGELOG.md000066400000000000000000000317751313243611400143030ustar00rootroot00000000000000## 1.6.2 (2017-07-15) * Fixed reflection warnings * Set default `AsyncContext` timeout to zero * Add `:async-timeout` option to override `AsyncContext` timeout ## 1.6.1 (2017-05-12) * Fixed unreported ClosedChannelExceptions in async Jetty * Updated Jetty to 9.2.21.v201701209 ## 1.6.0 (2017-05-02) * Functionally the same as 1.6.0-RC3. ## 1.6.0-RC3 (2017-04-18) * Fixed erroneous type hint (#286) * Fixed missing arguments in servlet functions (#287 and #288) * Fixed missing `AsyncContext` complete after `sendError` * Updated SPEC to clarify how to handle request headers with the same name ## 1.6.0-RC2 (2017-04-07) * Fixed missing `:allow-symlinks?` option in `wrap-resource` (#284) ## 1.6.0-RC1 (2017-03-10) * Fixed reflection warnings (#278) * Added support for HTML `_charset_` field in `wrap-multipart-params` (#276) * Added support for `SameSite` cookie attribute (#275) * Added support for unicode in `wrap-keyword-params` (#271) * Added preload for Cipher class to improve cookie store performance (#267) ## 1.6.0-beta7 (2017-01-10) * Backported path traversal vulnerability fix from version 1.5.1 * Fixed reflection warnings in `ring.core.protocols` * Stopped `wrap-stacktrace` HTML from overflowing ## 1.6.0-beta6 (2016-09-06) * Updated Commons FileUpload to 1.3.2 * Fixed missing argument in `ring.middleware.sesson/session-request` * Fixed intermittent 404 response in async Jetty adapter ## 1.6.0-beta5 (2016-08-10) * Fixed bug in Jetty adapter that stopped async responses from completing * Removed unnecessary tools.reader dependency ## 1.6.0-beta4 (2016-07-13) * Renamed `ResponseBody` protocol to `StreamableResponseBody` * Renamed `write-body` protocol method to `write-body-to-stream` * Added `ring.util.response/get-charset` function * Added response map to `write-body-to-stream` for character encoding of strings ## 1.6.0-beta3 (2016-07-09) * Fixed responses closing when output stream is written asynchronously ## 1.6.0-beta2 (2016-07-07) * Updated SPEC to accept three-arity asynchronous handlers * Updated all middleware to work with asynchronous handlers * Updated minimum servlet dependency in ring-servlet to 3.1 * Updated ring-servlet to use `javax.servlet.AsyncContext` with async handlers * Updated ring-jetty-adapter to support async handlers when `:async?` option is true ## 1.6.0-beta1 (2016-06-23) * Added `ring.core.protocols` namespace * Updated SPEC to accept response bodies that satisfy `ring.core.protocols/ResponseBody` ## 1.5.1 (2017-01-10) * Fixed path-traversal vulnerability in `resource-response` function ## 1.5.0 (2016-06-08) * Added `:http?` option to Jetty adapter to allow HTTP to be turned off * Added `:send-server-version?` option to Jetty adapter * Added `:exclude-ciphers` and `:exclude-protocols` options to Jetty adapter * Added a process listener function to `wrap-multipart-params` * Made `file-response` prefer HTML files as indexes over other formats * Made `wrap-reload` keep throwing compile exceptions, so errors aren't lost * Fixed issue with `:recreate` metadata not being removed from sessions * Fixed exception in `wrap-nested-params` on bad input * Updated Ring-Codec to 1.0.1 to fix exceptions on bad URL encoding ## 1.4.0 (2015-07-09) * Updated minimum Clojure version to 1.5.1 * Updated Jetty to 9.2.10 as Jetty 7 is at EOL (adapter now needs JDK 7 or later) * Added four new Jetty 9 specific options to Jetty adapter * Added :protocol key to the request map in the SPEC * Added class :loader option to wrap-resource and resource-response * Fixed lowercase header bug when working with non-english locales * Added optional status argument to ring.util.response/redirect * Added ring.util.response/resource-data multimethod * Added functionality to regenerate sessions using :recreate metadata * Fixed not-modified middleware affecting POSTs and 404s * Added find-header and update-header to ring.util.response * Fixed charset case sensitivity bug * Updated clj-stacktrace, tools.reader, clj-time and Apache Commons FileUpload * Fixed temporary filename leak in multipart-params middleware * Updated wrap-file to accept java.io.File instances * Updated ns-tracker to 0.3.0 to with more robust namespace parsing * Fixed issue with clj-time cookie expiry dates on non-English locales ## 1.3.2 (2014-11-27) * Ensure Jetty adapter threadpool is cleaned when server fails to start * Fixed NPE in resource-response for directory resources in jar files * Stopped ring.util.servlet/make-service being called every request * Made wrap-nested-params safe to use with already nested params * Fixed form field encoding in wrap-multipart-params * Added mimetype for HTML5 application cache manifest ## 1.3.1 (2014-08-24) * Support HEAD requests in ring.middleware.resource/resource-request * Fix handling of nested parameters with names that include newlines ## 1.3.0 (2014-06-02) * Deprecated :content-type, :content-length and :character-encoding keys in SPEC * Removed deprecated keys from source code * Added content-length, content-type and character-encoding to ring.util.request * Added urlencoded-form? to ring.util.request * Fixed 304 not-modified responses to set content-length header * Added options to wrap-cookies to specify encoder and decoder functions * Fixed wrap-head middleware when response is nil * Cryptography improvements; RNG faster under Linux * Jetty adapter accepts filepaths for :truststore option * Added :min-threads, :max-queued and :max-idle-time options to Jetty adapter * Fixed stacktrace middleware to handle assertion errors * Added optional body to ring.util.response/created function * Added :servlet-context-path to requests from servlet containers * Added mimetypes for edn and dart * Updated ns-tracker, clj-stacktrace and clj-time dependencies ## 1.2.2 (2014-03-13) * Cookie middleware now adheres to RFC 6265 * Fix for wrap-nested-params middleware * Update tools.reader version ## 1.2.1 (2013-10-28) * Fix for resources in jar files created with Leiningen 2.3.3 or above * Fix for UTF-8 characters in resource filenames * javax.servlet now a provided dependency ## 1.2.0 (2013-07-08) * Refactor of middleware to support async systems like Pedestal * Added wrap-not-modified middleware * Deprecated wrap-file-info middleware * Added ring.util.request namespace * file-response and resource-response include content-length and last-modifed headers * Use of dedicated EDN reader for security * Prettier wrap-stacktrace middleware * Support for :path-info and :context keys * Factored out encoding/decoding of data into ring-codec library * Fixed bug with :session-cookie-attrs not working if cookie not set * Fixed bug with last-modified dates on Windows * Fixed case-sensitivity issues in middleware handling headers * Added get-header, created and url-response to ring.util.response ## 1.1.8 (2013-01-20) * Updated ns-tracker dependency to fix issue with Clojure 1.5.0 * Updated clj-stacktrace dependency to fix exception reporting ## 1.1.7 (2013-01-12) * Secuity bug fix. See: http://goo.gl/DTRhn ## 1.1.6 (2012-09-22) * Removed default charset being incorrectly set on images * Fixed another bug in wrap-reload by updating ns-tracker version ## 1.1.5 (2012-09-03) * Fixed hanging when compiling handler with wrap-multiparm-params middleware ## 1.1.4 (2012-09-02) * Fixed bug in wrap-reload by updating ns-tracker version ## 1.1.3 (2012-08-22) * Fixed wrap-multipart creating default store each request * Fixed wrap-session :root option default overriding :cookie-attrs * Fixed potential security issue where users could force a custom session cookie name ## 1.1.2 (2012-08-12) * Fixed bug with content-type parameters in adapter and servlets * Fixed bug with temp-file store spawning too many threads ## 1.1.1 (2012-06-16) * Fixed bug with url-decoding "$" * Fixed bug with trust-store password ## 1.1.0 (2012-04-23) * Support for SSL client certificates in Jetty adapter * Jetty adapter dependency upgraded to 7.6.1 * wrap-cookies support for Joda-Time objects in expires and max-age attributes * Added wrap-head middleware * wrap-file middleware has option to follow symlinks * Added form-encode and form-decode to ring.util.codec * Fixed url-encode and url-decode to handle "+" correctly * Added ring.util.io namespace * Deprecated ring.util.test namespace * Hiccup ring-devel dependency upgraded to 1.0.0 * Added more functions to ring.util.response * Default number of Jetty adapter threads is now 50 * Support for KeyStore instances in Jetty adapter * Jetty configurator option now always applied last ## 1.0.2 (2012-01-25) * Updated clj-stacktrace to 0.2.4 to fix swank-clojure issue ## 1.0.1 (2011-12-18) * Workaround for [CLJ-885](http://dev.clojure.org/jira/browse/CLJ-885) ## 1.0.0 (2011-12-11) * Multipart parameters with same name correctly create vector of values * Fixed exception when resource-response is passed a directory * wrap-reload middleware changed to act like wrap-reload-modified * wrap-keyword-params ignores parameter names that cannot be keywords * wrap-keyword-params can be safely applied multiple times * Removed ring.middleware.static * Servlet outputstream no longer explicitly closed and flushed * Downgraded Jetty from 6.1.26 to 6.1.25 to solve socket issue * Jetty SSL adapter respects the :host option * Cookies can be set as http-only * Fixed wrap-params for non-UTF8-encoded POST requests * Fixed wrap-multipart-params bug that occurs in Clojure 1.3.0 * Better error reporting on invalid cookie attributes in wrap-cookies ## 0.3.11 (2011-07-14) * Multipart parameter storage backends (temp-file and byte-array) * Added redirect-after-post utility function * Character encoding of response set from content type ## 0.3.10 (2011-06-28) * Updated Hiccup to 0.3.6 for Clojure 1.3.0 compatibility ## 0.3.9 (2011-06-26) * wrap-params no longer excepts on invalid urlencoded query string * ring.util.servlet accepts multiple headers of the same name ## 0.3.8 (2011-04-23) * resource-response returns File object when possible * Added resource middleware * Stacktrace middleware displays causes (nested exceptions) * Bug fixes and refactor of stacktrace middleware ## 0.3.7 (2011-03-05) * Lint middleware recognises ISeq as valid response body * Added ring.util.mime-types namespace * Added content-type middleware ## 0.3.6 (2011-02-16) * Session and flash middleware handle nils without excepting * Cookie session store compares HMAC with constant-time function ## 0.3.5 (2010-11-27) * Context classloader now used for resource responses * Removed HttpCore adapter from repository * InputStream response body guaranteed to close * Updated Jetty dependencies to 6.1.26 ## 0.3.4 (2010-11-16) * wrap-cookies no longer overwrites existing Set-Cookie header * String response bodies no longer have extra newline ## 0.3.3 (2010-10-31) * Added console logging for ring.handler.dump and ring.middleware.stacktrace * Removed runtime dependency on clojure.contrib ## 0.3.2 (2010-10-11) * Added nested-params middleware ## 0.3.1 (2010-09-26) * Fixed multipart string encoding bug * Memory sessions can now take a user-defined atom as an argument * file-info middleware date checking improved * Added option map to file middleware * Jetty adapter :configurator option can now set Jetty handlers ## 0.3.0 (2010-09-19) * Updated Clojure and Clojure-Contrib version to 1.2.0 ## 0.2.6 (2010-09-09) * Fixed non-string param values in keyword params middleware ## 0.2.5 (2010-07-06) * Hopefully the last flash middleware fix we'll need * Added ring.util.response/resource-response function ## 0.2.4 (2010-07-04) * Fixed race condition in file-info middleware date parsing * Forced US locale for file-info middleware date parsing * Fixed NPE in multipart-params middleware when field is nil * Fixed another flash middleware bug that was wiping out session data ## 0.2.3 (2010-06-17) * Code updated to be more Clojure 1.2 compatible * Fixed bug in r.m.flash that was wiping out the session * Added If-Modified-Since support to r.m.file-info * Added ring.util.response/header * Added :root key to r.m.session as a shortcut to cookie path attribute * Updated ring-devel to use Hiccup instead of clj-html * Session cookie attributes can now be set by adding a :session-cookie-attrs key to the response. ## 0.2.2 (2010-05-16) * Introduce middleware for session flash * Cookie middleware made to work for browsers that don't follow cookie RFC (which is most of them) ## 0.2.1 (2010-05-05) * Depend on javax.servlet instead of org.mortbay.jetty for Servlet API artifact ## 0.2.0 (2010-03-28) * Distribute Ring as separate Maven artifacts: `ring-core`, `ring-servlet`, `ring-devel`, `ring-jetty-adapter`, and `ring-http-core-adapter` * The `ring` artifact now just depends on all of these granular artifacts * Build with Leiningen * Test with `clojure.test` * Depend only on stable point-released libraries * Introduce new middlewares for params, cookies, sessions * Intro new utils for encoding/decoding, forming responses, and unit testing * No longer require namespacing of request and response keys * More documentation, including autodocs * Various bugfixes ## 0.1.0 (2009-09-06) * First numbered Ring release * Adopt ring.{handler,middleware,adapter,util}.* namespace framework ring-1.6.2/CONTRIBUTORS.md000066400000000000000000000021401313243611400147310ustar00rootroot00000000000000* Mark McGranaghan * James Reeves * Christophe Grand * Richard Newman * Seth Buntin * David Santiago * Aaron Bedra * Aaron France * Aku Kotkavuo * Alexander Solovyov * Allen Rohner * Andrew Cholakian * Andy Fingerhut * Antti Rasinen * Baishampayan Ghose * Bill Caputo * Christoffer Sawicki * Colin Jones * Daniel Janus * David Powell * Gabriel Horner * Giacomo Ritucci * Günter Glück * Herwig Hochleitner * Ignacio Thayer * Iwan van der Kleijn * Jesper André Lyngesen Pedersen * Joseph Wilk * Josh Comer * Jude Chao * Juergen Hoetzel * Kevin J. Lynagh * Kurman Karabukaev * Kushal Pisavadia * Lake Denman * Gabriel Horner * Marko Kocic * Marko Topolnik * Marshall Bockrath-Vandegrift * Matt Furden * Matthew Courtney * Max Riveiro * Nahuel Greco * Nate Young * Nick Zalabak * Oliver Powell * Paul Schorfheide * Pepijn de Vos * Peter Garbers * Petr Gladkikh * Pierre-Yves Ritschard * Roman Scherer * Ryan Fowler * Sebastián Bernardo Galkin * Steven Degutis * Stuart Halloway * Tero Parviainen * Tobias Löfgren * Tom Denley * Tommi Reiman * Trent Ogren * Trevor Wennblom * Tyler Hobbs * Vadim Platonov * Yuri Niyazov ring-1.6.2/LICENSE000066400000000000000000000020531313243611400134620ustar00rootroot00000000000000Copyright (c) 2009-2010 Mark McGranaghan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.ring-1.6.2/README.md000066400000000000000000000037151313243611400137420ustar00rootroot00000000000000# Ring [![Build Status](https://travis-ci.org/ring-clojure/ring.svg?branch=master)](https://travis-ci.org/ring-clojure/ring) Ring is a Clojure web applications library inspired by Python's WSGI and Ruby's Rack. By abstracting the details of HTTP into a simple, unified API, Ring allows web applications to be constructed of modular components that can be shared among a variety of applications, web servers, and web frameworks. The [SPEC][1] file at the root of this distribution provides a complete description of the Ring interface. [1]: https://github.com/ring-clojure/ring/blob/master/SPEC ## Upgrade Notice From version 1.2.1 onward, the ring/ring-core package no longer comes with the `javax.servlet/servlet-api` package as a dependency (see issue [#89][2]). If you are using the `ring/ring-core` namespace on its own, you may run into errors when executing tests or running alternative adapters. To resolve this, include the following dependency in your dev profile: [javax.servlet/servlet-api "2.5"] [2]: https://github.com/ring-clojure/ring/pull/89 ## Libraries * ring-core - essential functions for handling parameters, cookies and more * ring-devel - functions for developing and debugging Ring applications * ring-servlet - construct Java servlets from Ring handlers * ring-jetty-adapter - a Ring adapter that uses the Jetty webserver ## Installation To include one of the above libraries, for example `ring-core`, add the following to your `:dependencies`: [ring/ring-core "1.6.2"] To include all of them: [ring "1.6.2"] ## Documentation * [Wiki](https://github.com/ring-clojure/ring/wiki) * [API docs](http://ring-clojure.github.com/ring) ## Community * [Google group](http://groups.google.com/group/ring-clojure) ## Thanks This project borrows heavily from Ruby's Rack and Python's WSGI; thanks to those communities for their work. ## License Copyright © 2009-2017 Mark McGranaghan, James Reeves & contributors. Released under the MIT license. ring-1.6.2/SPEC000066400000000000000000000112561313243611400131370ustar00rootroot00000000000000=== Ring Spec (1.4) Ring is defined in terms of handlers, middleware, adapters, requests maps, and response maps, each of which are described below. == Handlers Ring handlers constitute the core logic of the web application. Handlers are implemented as Clojure functions. A synchronous handler takes 1 argument, a request map, and returns a response map. An asynchronous handler takes 3 arguments: a request map, a callback function for sending a response and a callback function for raising an exception. The response callback takes a response map as its argument. The exception callback takes an exception as its argument. A handler function may simultaneously support synchronous and asynchronous behavior by accepting both arities. == Middleware Ring middleware augments the functionality of handlers by invoking them in the process of generating responses. Typically middleware will be implemented as a higher-order function that takes one or more handlers and configuration options as arguments and returns a new handler with the desired compound behavior. == Adapters Handlers are run via Ring adapters, which are in turn responsible for implementing the HTTP protocol and abstracting the handlers that they run from the details of the protocol. Adapters are implemented as functions of two arguments: a handler and an options map. The options map provides any needed configuration to the adapter, such as the port on which to run. Once initialized, adapters receive HTTP requests, parse them to construct a request map, and then invoke their handler with this request map as an argument. Once the handler returns a response map, the adapter uses it to construct and send an HTTP response to the client. == Request Map A request map is a Clojure map containing at least the following keys and corresponding values: :server-port (Required, Integer) The port on which the request is being handled. :server-name (Required, String) The resolved server name, or the server IP address. :remote-addr (Required, String) The IP address of the client or the last proxy that sent the request. :uri (Required, String) The request URI, excluding the query string and the "?" separator. Must start with "/". :query-string (Optional, String) The query string, if present. :scheme (Required, clojure.lang.Keyword) The transport protocol, must be one of :http or :https. :request-method (Required, clojure.lang.Keyword) The HTTP request method, must be a lowercase keyword corresponding to a HTTP request method, such as :get or :post. :protocol (Required, String) The protocol the request was made with, e.g. "HTTP/1.1". :content-type [DEPRECATED] (Optional, String) The MIME type of the request body, if known. :content-length [DEPRECATED] (Optional, Integer) The number of bytes in the request body, if known. :character-encoding [DEPRECATED] (Optional, String) The name of the character encoding used in the request body, if known. :ssl-client-cert (Optional, java.security.cert.X509Certificate) The SSL client certificate, if supplied. :headers (Required, clojure.lang.IPersistentMap) A Clojure map of downcased header name Strings to corresponding header value Strings. When there are multiple headers with the same name, the header values are concatenated together, separated by the "," character. :body (Optional, java.io.InputStream) An InputStream for the request body, if present. == Response Map A response map is a Clojure map containing at least the following keys and corresponding values: :status (Required, Integer) The HTTP status code, must be greater than or equal to 100. :headers (Required, clojure.lang.IPersistentMap) A Clojure map of HTTP header names to header values. These values may be either Strings, in which case one name/value header will be sent in the HTTP response, or a seq of Strings, in which case a name/value header will be sent for each such String value. :body (Optional, ring.core.protocols/StreamableResponseBody) A representation of the response body, if a response body is appropriate for the response's status code. The response body must satisfy the StreamableResponseBody protocol located in the ring.core.protocols namespace. By default the protocol is satisfied by the following types: String: Contents are sent to the client as-is. ISeq: Each element of the seq is sent to the client as a string. File: Contents at the specified location are sent to the client. The server may use an optimized method to send the file if such a method is available. InputStream: Contents are consumed from the stream and sent to the client. When the stream is exhausted, it is .close'd. ring-1.6.2/checkouts/000077500000000000000000000000001313243611400144455ustar00rootroot00000000000000ring-1.6.2/checkouts/ring-core000077700000000000000000000000001313243611400202732../ring-coreustar00rootroot00000000000000ring-1.6.2/checkouts/ring-devel000077700000000000000000000000001313243611400206112../ring-develustar00rootroot00000000000000ring-1.6.2/checkouts/ring-jetty-adapter000077700000000000000000000000001313243611400237452../ring-jetty-adapterustar00rootroot00000000000000ring-1.6.2/checkouts/ring-servlet000077700000000000000000000000001313243611400215632../ring-servletustar00rootroot00000000000000ring-1.6.2/project.clj000066400000000000000000000016151313243611400146200ustar00rootroot00000000000000(defproject ring "1.6.2" :description "A Clojure web applications library." :url "https://github.com/ring-clojure/ring" :license {:name "The MIT License" :url "http://opensource.org/licenses/MIT"} :dependencies [[org.clojure/clojure "1.5.1"] [ring/ring-core "1.6.2"] [ring/ring-devel "1.6.2"] [ring/ring-jetty-adapter "1.6.2"] [ring/ring-servlet "1.6.2"]] :plugins [[lein-sub "0.2.4"] [lein-codox "0.10.3"]] :sub ["ring-core" "ring-devel" "ring-jetty-adapter" "ring-servlet"] :codox {:output-path "codox" :source-uri "http://github.com/ring-clojure/ring/blob/{version}/{filepath}#L{line}" :source-paths ["ring-core/src" "ring-devel/src" "ring-jetty-adapter/src" "ring-servlet/src"]}) ring-1.6.2/ring-core/000077500000000000000000000000001313243611400143425ustar00rootroot00000000000000ring-1.6.2/ring-core/project.clj000066400000000000000000000016071313243611400165060ustar00rootroot00000000000000(defproject ring/ring-core "1.6.2" :description "Ring core libraries." :url "https://github.com/ring-clojure/ring" :scm {:dir ".."} :license {:name "The MIT License" :url "http://opensource.org/licenses/MIT"} :dependencies [[org.clojure/clojure "1.5.1"] [ring/ring-codec "1.0.1"] [commons-io "2.5"] [commons-fileupload "1.3.2"] [clj-time "0.11.0"] [crypto-random "1.2.0"] [crypto-equality "1.0.0"]] :aliases {"test-all" ["with-profile" "default:+1.6:+1.7:+1.8" "test"]} :profiles {:provided {:dependencies [[javax.servlet/servlet-api "2.5"]]} :dev {:dependencies [[javax.servlet/servlet-api "2.5"]]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]} :1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]}}) ring-1.6.2/ring-core/src/000077500000000000000000000000001313243611400151315ustar00rootroot00000000000000ring-1.6.2/ring-core/src/ring/000077500000000000000000000000001313243611400160705ustar00rootroot00000000000000ring-1.6.2/ring-core/src/ring/core/000077500000000000000000000000001313243611400170205ustar00rootroot00000000000000ring-1.6.2/ring-core/src/ring/core/protocols.clj000066400000000000000000000027421313243611400215430ustar00rootroot00000000000000(ns ring.core.protocols "Protocols necessary for Ring." {:added "1.6"} (:import [java.io Writer OutputStream]) (:require [clojure.java.io :as io] [ring.util.response :as response])) (defprotocol ^{:added "1.6"} StreamableResponseBody "A protocol for writing data to the response body via an output stream." (write-body-to-stream [body response output-stream] "Write a value representing a response body to an output stream. The stream will be closed after the value had been written.")) (defn- ^Writer response-writer [response output-stream] (if-let [charset (response/get-charset response)] (io/writer output-stream :encoding charset) (io/writer output-stream))) (extend-protocol StreamableResponseBody String (write-body-to-stream [body response output-stream] (with-open [writer (response-writer response output-stream)] (.write writer body))) clojure.lang.ISeq (write-body-to-stream [body response output-stream] (with-open [writer (response-writer response output-stream)] (doseq [chunk body] (.write writer (str chunk))))) java.io.InputStream (write-body-to-stream [body _ ^OutputStream output-stream] (with-open [out output-stream, body body] (io/copy body out))) java.io.File (write-body-to-stream [body _ ^OutputStream output-stream] (with-open [out output-stream] (io/copy body out))) nil (write-body-to-stream [_ _ ^java.io.OutputStream output-stream] (.close output-stream))) ring-1.6.2/ring-core/src/ring/middleware/000077500000000000000000000000001313243611400202055ustar00rootroot00000000000000ring-1.6.2/ring-core/src/ring/middleware/content_type.clj000066400000000000000000000030141313243611400234100ustar00rootroot00000000000000(ns ring.middleware.content-type "Middleware for automatically adding a content type to response maps." (:require [ring.util.mime-type :refer [ext-mime-type]] [ring.util.response :refer [content-type get-header]])) (defn content-type-response "Adds a content-type header to response. See: wrap-content-type." {:added "1.2"} ([response request] (content-type-response response request {})) ([response request options] (if response (if (get-header response "Content-Type") response (let [mime-type (ext-mime-type (:uri request) (:mime-types options))] (content-type response (or mime-type "application/octet-stream"))))))) (defn wrap-content-type "Middleware that adds a content-type header to the response if one is not set by the handler. Uses the ring.util.mime-type/ext-mime-type function to guess the content-type from the file extension in the URI. If no content-type can be found, it defaults to 'application/octet-stream'. Accepts the following options: :mime-types - a map of filename extensions to mime-types that will be used in addition to the ones defined in ring.util.mime-types/default-mime-types" ([handler] (wrap-content-type handler {})) ([handler options] (fn ([request] (-> (handler request) (content-type-response request options))) ([request respond raise] (handler request (fn [response] (respond (content-type-response response request options))) raise))))) ring-1.6.2/ring-core/src/ring/middleware/cookies.clj000066400000000000000000000140051313243611400223330ustar00rootroot00000000000000(ns ring.middleware.cookies "Middleware for parsing and generating cookies." (:import [org.joda.time DateTime Interval]) (:require [ring.util.codec :as codec] [clojure.string :as str] [clj-time.core :refer [in-seconds]] [clj-time.format :refer [formatters unparse with-locale]] [ring.util.parsing :refer [re-token]])) (def ^{:private true, :doc "RFC6265 cookie-octet"} re-cookie-octet #"[!#$%&'()*+\-./0-9:<=>?@A-Z\[\]\^_`a-z\{\|\}~]") (def ^{:private true, :doc "RFC6265 cookie-value"} re-cookie-value (re-pattern (str "\"" re-cookie-octet "*\"|" re-cookie-octet "*"))) (def ^{:private true, :doc "RFC6265 set-cookie-string"} re-cookie (re-pattern (str "\\s*(" re-token ")=(" re-cookie-value ")\\s*[;,]?"))) (def ^{:private true :doc "Attributes defined by RFC6265 that apply to the Set-Cookie header."} set-cookie-attrs {:domain "Domain", :max-age "Max-Age", :path "Path" :secure "Secure", :expires "Expires", :http-only "HttpOnly" :same-site "SameSite"}) (def ^{:private true :doc "Values defined by RFC6265 that apply to the SameSite cookie attribute header."} same-site-values {:strict "Strict" :lax "Lax"}) (def ^:private rfc822-formatter (with-locale (formatters :rfc822) java.util.Locale/US)) (defn- parse-cookie-header "Turn a HTTP Cookie header into a list of name/value pairs." [header] (for [[_ name value] (re-seq re-cookie header)] [name value])) (defn- strip-quotes "Strip quotes from a cookie value." [value] (str/replace value #"^\"|\"$" "")) (defn- decode-values [cookies decoder] (for [[name value] cookies] (if-let [value (decoder (strip-quotes value))] [name {:value value}]))) (defn- parse-cookies "Parse the cookies from a request map." [request encoder] (if-let [cookie (get-in request [:headers "cookie"])] (->> cookie parse-cookie-header ((fn [c] (decode-values c encoder))) (remove nil?) (into {})) {})) (defn- write-value "Write the main cookie value." [key value encoder] (encoder {key value})) (defn- valid-attr? "Is the attribute valid?" [[key value]] (and (contains? set-cookie-attrs key) (not (.contains (str value) ";")) (case key :max-age (or (instance? Interval value) (integer? value)) :expires (or (instance? DateTime value) (string? value)) :same-site (contains? same-site-values value) true))) (defn- write-attr-map "Write a map of cookie attributes to a string." [attrs] {:pre [(every? valid-attr? attrs)]} (for [[key value] attrs] (let [attr-name (name (set-cookie-attrs key))] (cond (instance? Interval value) (str ";" attr-name "=" (in-seconds value)) (instance? DateTime value) (str ";" attr-name "=" (unparse rfc822-formatter value)) (true? value) (str ";" attr-name) (false? value) "" (= :same-site key) (str ";" attr-name "=" (same-site-values value)) :else (str ";" attr-name "=" value))))) (defn- write-cookies "Turn a map of cookies into a seq of strings for a Set-Cookie header." [cookies encoder] (for [[key value] cookies] (if (map? value) (apply str (write-value key (:value value) encoder) (write-attr-map (dissoc value :value))) (write-value key value encoder)))) (defn- set-cookies "Add a Set-Cookie header to a response if there is a :cookies key." [response encoder] (if-let [cookies (:cookies response)] (update-in response [:headers "Set-Cookie"] concat (doall (write-cookies cookies encoder))) response)) (defn cookies-request "Parses cookies in the request map. See: wrap-cookies." {:added "1.2"} ([request] (cookies-request request {})) ([request options] (let [{:keys [decoder] :or {decoder codec/form-decode-str}} options] (if (request :cookies) request (assoc request :cookies (parse-cookies request decoder)))))) (defn cookies-response "For responses with :cookies, adds Set-Cookie header and returns response without :cookies. See: wrap-cookies." {:added "1.2"} ([response] (cookies-response response {})) ([response options] (let [{:keys [encoder] :or {encoder codec/form-encode}} options] (-> response (set-cookies encoder) (dissoc :cookies))))) (defn wrap-cookies "Parses the cookies in the request map, then assocs the resulting map to the :cookies key on the request. Accepts the following options: :decoder - a function to decode the cookie value. Expects a function that takes a string and returns a string. Defaults to URL-decoding. :encoder - a function to encode the cookie name and value. Expects a function that takes a name/value map and returns a string. Defaults to URL-encoding. Each cookie is represented as a map, with its value being held in the :value key. A cookie may optionally contain a :path, :domain or :port attribute. To set cookies, add a map to the :cookies key on the response. The values of the cookie map can either be strings, or maps containing the following keys: :value - the new value of the cookie :path - the subpath the cookie is valid for :domain - the domain the cookie is valid for :max-age - the maximum age in seconds of the cookie :expires - a date string at which the cookie will expire :secure - set to true if the cookie requires HTTPS, prevent HTTP access :http-only - set to true if the cookie is valid for HTTP and HTTPS only (ie. prevent JavaScript access) :same-site - set to :strict or :lax to set SameSite attribute of the cookie" ([handler] (wrap-cookies handler {})) ([handler options] (fn ([request] (-> request (cookies-request options) handler (cookies-response options))) ([request respond raise] (handler (cookies-request request options) (fn [response] (respond (cookies-response response options))) raise))))) ring-1.6.2/ring-core/src/ring/middleware/file.clj000066400000000000000000000040751313243611400216240ustar00rootroot00000000000000(ns ring.middleware.file "Middleware to serve files from a directory. Most of the time you should prefer ring.middleware.resource instead, as this middleware will not work with files in jar or war files." (:require [ring.util.codec :as codec] [ring.util.response :as response] [ring.util.request :as request] [ring.middleware.head :as head] [clojure.java.io :as io])) (defn- ensure-dir "Ensures that a directory exists at the given path, throwing if one does not." [dir-path] (let [dir (io/as-file dir-path)] (if-not (.exists dir) (throw (Exception. (format "Directory does not exist: %s" dir-path)))))) (defn file-request "If request matches a static file, returns it in a response. Otherwise returns nil. See: wrap-file." {:added "1.2"} ([request root-path] (file-request request root-path {})) ([request root-path options] (let [options (merge {:root (str root-path) :index-files? true :allow-symlinks? false} options)] (if (#{:get :head} (:request-method request)) (let [path (subs (codec/url-decode (request/path-info request)) 1)] (-> (response/file-response path options) (head/head-response request))))))) (defn wrap-file "Wrap an handler such that the directory at the given root-path is checked for a static file with which to respond to the request, proxying the request to the wrapped handler if such a file does not exist. Accepts the following options: :index-files? - look for index.* files in directories, defaults to true :allow-symlinks? - serve files through symbolic links, defaults to false" ([handler root-path] (wrap-file handler root-path {})) ([handler root-path options] (ensure-dir root-path) (fn ([request] (or (file-request request root-path options) (handler request))) ([request respond raise] (if-let [response (file-request request root-path options)] (respond response) (handler request respond raise)))))) ring-1.6.2/ring-core/src/ring/middleware/file_info.clj000066400000000000000000000062671313243611400226440ustar00rootroot00000000000000(ns ring.middleware.file-info "Middleware to add Last-Modified and Content-Type headers to file responses. This middleware is deprecated. Prefer the ring.middleware.content-type and ring.middleware.not-modified middleware instead." (:require [ring.util.response :as res] [ring.util.mime-type :refer [ext-mime-type]] [ring.util.io :refer [last-modified-date]]) (:import [java.io File] [java.util Date Locale TimeZone] [java.text SimpleDateFormat])) (defn- guess-mime-type "Returns a String corresponding to the guessed mime type for the given file, or application/octet-stream if a type cannot be guessed." [^File file mime-types] (or (ext-mime-type (.getPath file) mime-types) "application/octet-stream")) (defn- ^SimpleDateFormat make-http-format "Formats or parses dates into HTTP date format (RFC 822/1123)." [] ;; SimpleDateFormat is not threadsafe, so return a new instance each time (doto (SimpleDateFormat. "EEE, dd MMM yyyy HH:mm:ss ZZZ" Locale/US) (.setTimeZone (TimeZone/getTimeZone "UTC")))) (defn- not-modified-since? "Has the file been modified since the last request from the client?" [{headers :headers :as req} last-modified] (if-let [modified-since (headers "if-modified-since")] (not (.before (.parse (make-http-format) modified-since) last-modified)))) (defn file-info-response "Adds headers to response as described in wrap-file-info." {:added "1.2", :deprecated "1.2"} ([response request] (file-info-response response request {})) ([response request mime-types] (let [body (:body response)] (if (instance? File body) (let [file-type (guess-mime-type body mime-types) file-length (.length ^File body) lmodified (last-modified-date body) response (-> response (res/content-type file-type) (res/header "Last-Modified" (.format (make-http-format) lmodified)))] (if (not-modified-since? request lmodified) (-> response (res/status 304) (res/header "Content-Length" 0) (assoc :body "")) (-> response (res/header "Content-Length" file-length)))) response)))) (defn wrap-file-info "Wrap a handler such that responses with a file for a body will have corresponding Content-Type, Content-Length, and Last Modified headers added if they can be determined from the file. If the request specifies a If-Modified-Since header that matches the last modification date of the file, a 304 Not Modified response is returned. If two arguments are given, the second is taken to be a map of file extensions to content types that will supplement the default, built-in map." {:deprecated "1.2"} ([handler] (wrap-file-info handler {})) ([handler mime-types] (fn ([request] (-> (handler request) (file-info-response request mime-types))) ([request respond raise] (handler request (fn [response] (respond (file-info-response response request mime-types))) raise))))) ring-1.6.2/ring-core/src/ring/middleware/flash.clj000066400000000000000000000031141313243611400217730ustar00rootroot00000000000000(ns ring.middleware.flash "Middleware that adds session-based flash store that persists only to the next request in the same session.") (defn flash-request "Adds :flash key to request from :_flash in session." {:added "1.2"} [request] (let [session (:session request) flash (:_flash session) session (dissoc session :_flash)] (assoc request :session session, :flash flash))) (defn flash-response "If response has a :flash key, saves it in :_flash of session for next request." {:added "1.2"} [response request] (let [{:keys [session flash]} request] (if response (let [session (if (contains? response :session) (response :session) session) session (if-let [flash (response :flash)] (assoc (response :session session) :_flash flash) session)] (if (or flash (response :flash) (contains? response :session)) (assoc response :session session) response))))) (defn wrap-flash "If a :flash key is set on the response by the handler, a :flash key with the same value will be set on the next request that shares the same session. This is useful for small messages that persist across redirects." [handler] (fn ([request] (let [request (flash-request request)] (-> (handler request) (flash-response request)))) ([request respond raise] (let [request (flash-request request)] (handler request (fn [response] (respond (flash-response response request))) raise))))) ring-1.6.2/ring-core/src/ring/middleware/head.clj000066400000000000000000000020321313243611400215750ustar00rootroot00000000000000(ns ring.middleware.head "Middleware to simplify replying to HEAD requests. A response to a HEAD request should be identical to a GET request, with the exception that a response to a HEAD request should have an empty body.") (defn head-request "Turns a HEAD request into a GET." {:added "1.2"} [request] (if (= :head (:request-method request)) (assoc request :request-method :get) request)) (defn head-response "Returns a nil body if original request was a HEAD." {:added "1.2"} [response request] (if (and response (= :head (:request-method request))) (assoc response :body nil) response)) (defn wrap-head "Middleware that turns any HEAD request into a GET, and then sets the response body to nil." {:added "1.1"} [handler] (fn ([request] (-> request head-request handler (head-response request))) ([request respond raise] (handler (head-request request) (fn [response] (respond (head-response response request))) raise)))) ring-1.6.2/ring-core/src/ring/middleware/keyword_params.clj000066400000000000000000000021501313243611400237240ustar00rootroot00000000000000(ns ring.middleware.keyword-params "Middleware that converts parameter keys in the request to keywords.") (defn- keyword-syntax? [s] (re-matches #"[\p{L}*+!_?-][\p{L}0-9*+!_?-]*" s)) (defn- keyify-params [target] (cond (map? target) (into {} (for [[k v] target] [(if (and (string? k) (keyword-syntax? k)) (keyword k) k) (keyify-params v)])) (vector? target) (vec (map keyify-params target)) :else target)) (defn keyword-params-request "Converts string keys in :params map to keywords. See: wrap-keyword-params." {:added "1.2"} [request] (update-in request [:params] keyify-params)) (defn wrap-keyword-params "Middleware that converts the any string keys in the :params map to keywords. Only keys that can be turned into valid keywords are converted. This middleware does not alter the maps under :*-params keys. These are left as strings." [handler] (fn ([request] (handler (keyword-params-request request))) ([request respond raise] (handler (keyword-params-request request) respond raise)))) ring-1.6.2/ring-core/src/ring/middleware/multipart_params.clj000066400000000000000000000161561313243611400242740ustar00rootroot00000000000000(ns ring.middleware.multipart-params "Middleware that parses multipart request bodies into parameters. This middleware is necessary to handle file uploads from web browsers. Ring comes with two different multipart storage engines included: ring.middleware.multipart-params.byte-array/byte-array-store ring.middleware.multipart-params.temp-file/temp-file-store" (:require [ring.util.codec :refer [assoc-conj]] [ring.util.request :as req] [ring.util.parsing :refer [re-charset]]) (:import [org.apache.commons.fileupload UploadContext FileItemIterator FileItemStream FileUpload ProgressListener] [org.apache.commons.io IOUtils])) (defn- progress-listener "Create a progress listener that calls the supplied function." [request progress-fn] (reify ProgressListener (update [this bytes-read content-length item-count] (progress-fn request bytes-read content-length item-count)))) (defn- multipart-form? "Does a request have a multipart form?" [request] (= (req/content-type request) "multipart/form-data")) (defn- request-context "Create an UploadContext object from a request map." {:tag UploadContext} [request encoding] (reify UploadContext (getContentType [this] (get-in request [:headers "content-type"])) (getContentLength [this] (or (req/content-length request) -1)) (contentLength [this] (or (req/content-length request) -1)) (getCharacterEncoding [this] encoding) (getInputStream [this] (:body request)))) (defn- file-item-iterator-seq "Create a lazy seq from a FileItemIterator instance." [^FileItemIterator it] (lazy-seq (if (.hasNext it) (cons (.next it) (file-item-iterator-seq it))))) (defn- file-item-seq "Create a seq of FileItem instances from a request context." [request progress-fn context] (let [upload (if progress-fn (doto (FileUpload.) (.setProgressListener (progress-listener request progress-fn))) (FileUpload.))] (file-item-iterator-seq (.getItemIterator ^FileUpload upload context)))) (defn- parse-content-type-charset [^FileItemStream item] (some->> (.getContentType item) (re-find re-charset) second)) (defn- parse-html5-charset [params] (when-let [charset (->> params (filter #(= (first %) "_charset_")) first second :bytes)] (String. ^bytes charset "US-ASCII"))) (defn- decode-string-values [fallback-encoding forced-encoding params] (let [html5-encoding (parse-html5-charset params)] (for [[k v] params] [k (if-let [^bytes bytes (:bytes v)] (String. bytes (str (or forced-encoding html5-encoding (:encoding v) fallback-encoding))) v)]))) (defn- parse-file-item "Parse a FileItemStream into a key-value pair. If the request is a file the supplied store function is used to save it." [^FileItemStream item store] [(.getFieldName item) (if (.isFormField item) {:bytes (IOUtils/toByteArray (.openStream item)) :encoding (parse-content-type-charset item)} (store {:filename (.getName item) :content-type (.getContentType item) :stream (.openStream item)}))]) (defn- parse-multipart-params "Parse a map of multipart parameters from the request." [request fallback-encoding forced-encoding store progress-fn] (->> (request-context request fallback-encoding) (file-item-seq request progress-fn) (map #(parse-file-item % store)) (decode-string-values fallback-encoding forced-encoding) (reduce (fn [m [k v]] (assoc-conj m k v)) {}))) (defn- load-var "Returns the var named by the supplied symbol, or nil if not found. Attempts to load the var namespace on the fly if not already loaded." [sym] (require (symbol (namespace sym))) (find-var sym)) (def ^:private default-store (delay (let [store 'ring.middleware.multipart-params.temp-file/temp-file-store func (load-var store)] (func)))) (defn multipart-params-request "Adds :multipart-params and :params keys to request. See: wrap-multipart-params." {:added "1.2"} ([request] (multipart-params-request request {})) ([request options] (let [store (or (:store options) @default-store) forced-encoding (:encoding options) req-encoding (or forced-encoding (:fallback-encoding options) (req/character-encoding request) "UTF-8") progress (:progress-fn options) params (if (multipart-form? request) (parse-multipart-params request req-encoding forced-encoding store progress) {})] (merge-with merge request {:multipart-params params} {:params params})))) (defn wrap-multipart-params "Middleware to parse multipart parameters from a request. Adds the following keys to the request map: :multipart-params - a map of multipart parameters :params - a merged map of all types of parameter The following options are accepted :encoding - character encoding to use for multipart parsing. Overrides the encoding specified in the request. If not specified, uses the encoding specified in a part named \"_charset_\", or the content type for each part, or request character encoding if the part has no encoding, or \"UTF-8\" if no request character encoding is set. :fallback-encoding - specifies the character encoding used in parsing if a part of the request does not specify encoding in its content type or no part named \"_charset_\" is present. Has no effect if :encoding is also set. :store - a function that stores a file upload. The function should expect a map with :filename, content-type and :stream keys, and its return value will be used as the value for the parameter in the multipart parameter map. The default storage function is the temp-file-store. :progress-fn - a function that gets called during uploads. The function should expect four parameters: request, bytes-read, content-length, and item-count." ([handler] (wrap-multipart-params handler {})) ([handler options] (fn ([request] (handler (multipart-params-request request options))) ([request respond raise] (handler (multipart-params-request request options) respond raise))))) ring-1.6.2/ring-core/src/ring/middleware/multipart_params/000077500000000000000000000000001313243611400235715ustar00rootroot00000000000000ring-1.6.2/ring-core/src/ring/middleware/multipart_params/byte_array.clj000066400000000000000000000013031313243611400264210ustar00rootroot00000000000000(ns ring.middleware.multipart-params.byte-array "A multipart storage engine for storing uploads as in-memory byte arrays." (:import [java.io InputStream] [org.apache.commons.io IOUtils])) (defn byte-array-store "Returns a function that stores multipart file parameters as an array of bytes. The multipart parameters will be stored as maps with the following keys: :filename - the name of the uploaded file :content-type - the content type of the uploaded file :bytes - an array of bytes containing the uploaded content" [] (fn [item] (-> (select-keys item [:filename :content-type]) (assoc :bytes (IOUtils/toByteArray ^InputStream (:stream item)))))) ring-1.6.2/ring-core/src/ring/middleware/multipart_params/temp_file.clj000066400000000000000000000045121313243611400262310ustar00rootroot00000000000000(ns ring.middleware.multipart-params.temp-file "A multipart storage engine for storing uploads in temporary files." (:require [clojure.java.io :as io]) (:import [java.io File])) (defn- background-thread [^Runnable f] (doto (Thread. f) (.setDaemon true) (.start))) (defmacro ^{:private true} do-every [delay & body] `(background-thread #(while true (Thread/sleep (* ~delay 1000)) (try ~@body (catch Exception ex#))))) (defn- expired? [^File file expiry-time] (< (.lastModified file) (- (System/currentTimeMillis) (* expiry-time 1000)))) (defn- remove-old-files [file-set expiry-time] (doseq [^File file @file-set] (when (expired? file expiry-time) (.delete file) (swap! file-set disj file)))) (defn- ^File make-temp-file [file-set] (let [temp-file (File/createTempFile "ring-multipart-" nil)] (swap! file-set conj temp-file) temp-file)) (defn- start-clean-up [file-set expires-in] (when expires-in (do-every expires-in (remove-old-files file-set expires-in)))) (defn- ensure-shutdown-clean-up [file-set] (.addShutdownHook (Runtime/getRuntime) (Thread. #(doseq [^File file @file-set] (.delete file))))) (defn temp-file-store "Returns a function that stores multipart file parameters as temporary files. Accepts the following options: :expires-in - delete temporary files older than this many seconds (defaults to 3600 - 1 hour) The multipart parameters will be stored as maps with the following keys: :filename - the name of the uploaded file :content-type - the content type of the upload file :tempfile - a File object that points to the temporary file containing the uploaded data :size - the size in bytes of the uploaded data" {:arglists '([] [options])} ([] (temp-file-store {:expires-in 3600})) ([{:keys [expires-in]}] (let [file-set (atom #{}) clean-up (delay (start-clean-up file-set expires-in))] (ensure-shutdown-clean-up file-set) (fn [item] (force clean-up) (let [temp-file (make-temp-file file-set)] (io/copy (:stream item) temp-file) (-> (select-keys item [:filename :content-type]) (assoc :tempfile temp-file :size (.length temp-file)))))))) ring-1.6.2/ring-core/src/ring/middleware/nested_params.clj000066400000000000000000000051461313243611400235320ustar00rootroot00000000000000(ns ring.middleware.nested-params "Middleware to convert a single-depth map of parameters to a nested map." (:require [ring.util.codec :refer [assoc-conj]])) (defn parse-nested-keys "Parse a parameter name into a list of keys using a 'C'-like index notation. For example: \"foo[bar][][baz]\" => [\"foo\" \"bar\" \"\" \"baz\"]" [param-name] (let [[_ k ks] (re-matches #"(?s)(.*?)((?:\[.*?\])*)" (name param-name)) keys (if ks (map second (re-seq #"\[(.*?)\]" ks)))] (cons k keys))) (defn- assoc-vec [m k v] (let [m (if (contains? m k) m (assoc m k []))] (assoc-conj m k v))) (defn- assoc-nested "Similar to assoc-in, but treats values of blank keys as elements in a list." [m [k & ks] v] (if k (if ks (let [[j & js] ks] (if (= j "") (assoc-vec m k (assoc-nested {} js v)) (assoc m k (assoc-nested (get m k {}) ks v)))) (if (map? m) (assoc-conj m k v) {k v})) v)) (defn- param-pairs "Return a list of name-value pairs for a parameter map." [params] (mapcat (fn [[name value]] (if (and (sequential? value) (not (coll? (first value)))) (for [v value] [name v]) [[name value]])) params)) (defn- nest-params "Takes a flat map of parameters and turns it into a nested map of parameters, using the function parse to split the parameter names into keys." [params parse] (reduce (fn [m [k v]] (assoc-nested m (parse k) v)) {} (param-pairs params))) (defn nested-params-request "Converts a request with a flat map of parameters to a nested map. See: wrap-nested-params." {:added "1.2"} ([request] (nested-params-request request {})) ([request options] (let [parse (:key-parser options parse-nested-keys)] (update-in request [:params] nest-params parse)))) (defn wrap-nested-params "Middleware to converts a flat map of parameters into a nested map. Accepts the following options: :key-parser - the function to use to parse the parameter names into a list of keys. Keys that are empty strings are treated as elements in a vector, non-empty keys are treated as elements in a map. Defaults to the parse-nested-keys function. For example: {\"foo[bar]\" \"baz\"} => {\"foo\" {\"bar\" \"baz\"}} {\"foo[]\" \"bar\"} => {\"foo\" [\"bar\"]}" ([handler] (wrap-nested-params handler {})) ([handler options] (fn ([request] (handler (nested-params-request request options))) ([request respond raise] (handler (nested-params-request request options) respond raise))))) ring-1.6.2/ring-core/src/ring/middleware/not_modified.clj000066400000000000000000000037321313243611400233440ustar00rootroot00000000000000(ns ring.middleware.not-modified "Middleware that returns a 304 Not Modified response for responses with Last-Modified headers." (:require [ring.util.time :refer [parse-date]] [ring.util.response :refer [status get-header header]] [ring.util.io :refer [close!]])) (defn- etag-match? [request response] (if-let [etag (get-header response "ETag")] (= etag (get-header request "if-none-match")))) (defn- ^java.util.Date date-header [response header] (if-let [http-date (get-header response header)] (parse-date http-date))) (defn- not-modified-since? [request response] (let [modified-date (date-header response "Last-Modified") modified-since (date-header request "if-modified-since")] (and modified-date modified-since (not (.before modified-since modified-date))))) (defn- read-request? [request] (#{:get :head} (:request-method request))) (defn- ok-response? [response] (= (:status response) 200)) (defn not-modified-response "Returns 304 or original response based on response and request. See: wrap-not-modified." {:added "1.2"} [response request] (if (and (read-request? request) (ok-response? response) (or (etag-match? request response) (not-modified-since? request response))) (do (close! (:body response)) (-> response (assoc :status 304) (header "Content-Length" 0) (assoc :body nil))) response)) (defn wrap-not-modified "Middleware that returns a 304 Not Modified from the wrapped handler if the handler response has an ETag or Last-Modified header, and the request has a If-None-Match or If-Modified-Since header that matches the response." {:added "1.2"} [handler] (fn ([request] (-> (handler request) (not-modified-response request))) ([request respond raise] (handler request (fn [response] (respond (not-modified-response response request))) raise)))) ring-1.6.2/ring-core/src/ring/middleware/params.clj000066400000000000000000000046361313243611400221730ustar00rootroot00000000000000(ns ring.middleware.params "Middleware to parse url-encoded parameters from the query string and request body." (:require [ring.util.codec :as codec] [ring.util.request :as req])) (defn- parse-params [params encoding] (let [params (codec/form-decode params encoding)] (if (map? params) params {}))) (defn assoc-query-params "Parse and assoc parameters from the query string with the request." {:added "1.3"} [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." {:added "1.2"} [request encoding] (merge-with merge request (if-let [body (and (req/urlencoded-form? request) (:body request))] (let [params (parse-params (slurp body :encoding encoding) encoding)] {:form-params params, :params params}) {:form-params {}, :params {}}))) (defn params-request "Adds parameters from the query string and the request body to the request map. See: wrap-params." {:added "1.2"} ([request] (params-request request {})) ([request options] (let [encoding (or (:encoding options) (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." ([handler] (wrap-params handler {})) ([handler options] (fn ([request] (handler (params-request request options))) ([request respond raise] (handler (params-request request options) respond raise))))) ring-1.6.2/ring-core/src/ring/middleware/resource.clj000066400000000000000000000032321313243611400225260ustar00rootroot00000000000000(ns ring.middleware.resource "Middleware for serving static resources." (:require [ring.util.codec :as codec] [ring.util.response :as response] [ring.util.request :as request] [ring.middleware.head :as head])) (defn resource-request "If request matches a static resource, returns it in a response map. Otherwise returns nil. See wrap-resource for the available options." {:added "1.2"} ([request root-path] (resource-request request root-path {})) ([request root-path options] (if (#{:head :get} (:request-method request)) (let [path (subs (codec/url-decode (request/path-info request)) 1)] (-> (response/resource-response path (assoc options :root root-path)) (head/head-response request)))))) (defn wrap-resource "Middleware that first checks to see whether the request map matches a static resource. If it does, the resource is returned in a response map, otherwise the request map is passed onto the handler. The root-path argument will be added to the beginning of the resource path. Accepts the following options: :loader - resolve the resource using this class loader :allow-symlinks? - allow symlinks that lead to paths outside the root classpath directories (defaults to false)" ([handler root-path] (wrap-resource handler root-path {})) ([handler root-path options] (fn ([request] (or (resource-request request root-path options) (handler request))) ([request respond raise] (if-let [response (resource-request request root-path options)] (respond response) (handler request respond raise)))))) ring-1.6.2/ring-core/src/ring/middleware/session.clj000066400000000000000000000112741313243611400223670ustar00rootroot00000000000000(ns ring.middleware.session "Middleware for maintaining browser sessions using cookies. Sessions are stored using types that adhere to the ring.middleware.session.store/SessionStore protocol. Ring comes with two stores included: ring.middleware.session.memory/memory-store ring.middleware.session.cookie/cookie-store" (:require [ring.middleware.cookies :as cookies] [ring.middleware.session.store :as store] [ring.middleware.session.memory :as mem])) (defn- session-options [options] {:store (options :store (mem/memory-store)) :cookie-name (options :cookie-name "ring-session") :cookie-attrs (merge {:path "/" :http-only true} (options :cookie-attrs) (if-let [root (options :root)] {:path root}))}) (defn- bare-session-request [request {:keys [store cookie-name]}] (let [req-key (get-in request [:cookies cookie-name :value]) session (store/read-session store req-key) session-key (if session req-key)] (merge request {:session (or session {}) :session/key session-key}))) (defn session-request "Reads current HTTP session map and adds it to :session key of the request. See: wrap-session." {:added "1.2"} ([request] (session-request request {})) ([request options] (-> request cookies/cookies-request (bare-session-request options)))) (defn- bare-session-response [response {session-key :session/key} {:keys [store cookie-name cookie-attrs]}] (let [new-session-key (if (contains? response :session) (if-let [session (response :session)] (if (:recreate (meta session)) (do (store/delete-session store session-key) (->> (vary-meta session dissoc :recreate) (store/write-session store nil))) (store/write-session store session-key session)) (if session-key (store/delete-session store session-key)))) session-attrs (:session-cookie-attrs response) cookie {cookie-name (merge cookie-attrs session-attrs {:value (or new-session-key session-key)})} response (dissoc response :session :session-cookie-attrs)] (if (or (and new-session-key (not= session-key new-session-key)) (and session-attrs (or new-session-key session-key))) (assoc response :cookies (merge (response :cookies) cookie)) response))) (defn session-response "Updates session based on :session key in response. See: wrap-session." {:added "1.2"} ([response request] (session-response response request {})) ([response request options] (if response (-> response (bare-session-response request options) cookies/cookies-response)))) (defn wrap-session "Reads in the current HTTP session map, and adds it to the :session key on the request. If a :session key is added to the response by the handler, the session is updated with the new value. If the value is nil, the session is deleted. Accepts the following options: :store - An implementation of the SessionStore protocol in the ring.middleware.session.store namespace. This determines how the session is stored. Defaults to in-memory storage using ring.middleware.session.store/memory-store. :root - The root path of the session. Any path above this will not be able to see this session. Equivalent to setting the cookie's path attribute. Defaults to \"/\". :cookie-name - The name of the cookie that holds the session key. Defaults to \"ring-session\". :cookie-attrs - A map of attributes to associate with the session cookie. Defaults to {:http-only true}. This may be overridden on a per-response basis by adding :session-cookie-attrs to the response." ([handler] (wrap-session handler {})) ([handler options] (let [options (session-options options)] (fn ([request] (let [request (session-request request options)] (-> (handler request) (session-response request options)))) ([request respond raise] (let [request (session-request request options)] (handler request (fn [response] (respond (session-response response request options))) raise))))))) ring-1.6.2/ring-core/src/ring/middleware/session/000077500000000000000000000000001313243611400216705ustar00rootroot00000000000000ring-1.6.2/ring-core/src/ring/middleware/session/cookie.clj000066400000000000000000000070531313243611400236400ustar00rootroot00000000000000(ns ring.middleware.session.cookie "A session storage engine that stores session data in encrypted cookies." (:require [ring.middleware.session.store :refer [SessionStore]] [ring.util.codec :as codec] [clojure.edn :as edn] [crypto.random :as random] [crypto.equality :as crypto]) (:import [java.security SecureRandom] [javax.crypto Cipher Mac] [javax.crypto.spec SecretKeySpec IvParameterSpec])) (def ^{:private true :doc "Algorithm to generate a HMAC."} hmac-algorithm "HmacSHA256") (def ^{:private true :doc "Type of encryption to use."} crypt-type "AES") (def ^{:private true :doc "Full algorithm to encrypt data with."} crypt-algorithm "AES/CBC/PKCS5Padding") ;; Ensure cipher-algorithm classes are preloaded (Cipher/getInstance crypt-algorithm) (defn- hmac "Generates a Base64 HMAC with the supplied key on a string of data." [key data] (let [mac (Mac/getInstance hmac-algorithm)] (.init mac (SecretKeySpec. key hmac-algorithm)) (codec/base64-encode (.doFinal mac data)))) (defn- encrypt "Encrypt a string with a key." [key data] (let [cipher (Cipher/getInstance crypt-algorithm) secret-key (SecretKeySpec. key crypt-type) iv (random/bytes (.getBlockSize cipher))] (.init cipher Cipher/ENCRYPT_MODE secret-key (IvParameterSpec. iv)) (->> (.doFinal cipher data) (concat iv) (byte-array)))) (defn- decrypt "Decrypt an array of bytes with a key." [key data] (let [cipher (Cipher/getInstance crypt-algorithm) secret-key (SecretKeySpec. key crypt-type) [iv data] (split-at (.getBlockSize cipher) data) iv-spec (IvParameterSpec. (byte-array iv))] (.init cipher Cipher/DECRYPT_MODE secret-key iv-spec) (String. (.doFinal cipher (byte-array data))))) (defn- get-secret-key "Get a valid secret key from a map of options, or create a random one from scratch." [options] (if-let [secret-key (:key options)] (if (string? secret-key) (.getBytes ^String secret-key) secret-key) (random/bytes 16))) (defn- ^String serialize [x] {:post [(= x (edn/read-string %))]} (pr-str x)) (defn- seal "Seal a Clojure data structure into an encrypted and HMACed string." [key data] (let [data (encrypt key (.getBytes (serialize data)))] (str (codec/base64-encode data) "--" (hmac key data)))) (defn- unseal "Retrieve a sealed Clojure data structure from a string" [key ^String string] (let [[data mac] (.split string "--") data (codec/base64-decode data)] (if (crypto/eq? mac (hmac key data)) (edn/read-string (decrypt key data))))) (deftype CookieStore [secret-key] SessionStore (read-session [_ data] (if data (unseal secret-key data))) (write-session [_ _ data] (seal secret-key data)) (delete-session [_ _] (seal secret-key {}))) (ns-unmap *ns* '->CookieStore) (defn- valid-secret-key? [key] (and (= (type (byte-array 0)) (type key)) (= (count key) 16))) (defn cookie-store "Creates an encrypted cookie storage engine. Accepts the following options: :key - The secret key to encrypt the session cookie. Must be exactly 16 bytes If no key is provided then a random key will be generated. Note that in that case a server restart will invalidate all existing session cookies." ([] (cookie-store {})) ([options] (let [key (get-secret-key options)] (assert (valid-secret-key? key) "the secret key must be exactly 16 bytes") (CookieStore. (get-secret-key options))))) ring-1.6.2/ring-core/src/ring/middleware/session/memory.clj000066400000000000000000000014121313243611400236700ustar00rootroot00000000000000(ns ring.middleware.session.memory "A session storage engine that stores session data in memory." (:require [ring.middleware.session.store :refer [SessionStore]]) (:import [java.util UUID])) (deftype MemoryStore [session-map] SessionStore (read-session [_ key] (@session-map key)) (write-session [_ key data] (let [key (or key (str (UUID/randomUUID)))] (swap! session-map assoc key data) key)) (delete-session [_ key] (swap! session-map dissoc key) nil)) (ns-unmap *ns* '->MemoryStore) (defn memory-store "Creates an in-memory session storage engine. Accepts an atom as an optional argument; if supplied, the atom is used to hold the session data." ([] (memory-store (atom {}))) ([session-atom] (MemoryStore. session-atom))) ring-1.6.2/ring-core/src/ring/middleware/session/store.clj000066400000000000000000000017771313243611400235320ustar00rootroot00000000000000(ns ring.middleware.session.store "Contains the protocol used to define all Ring session storage engines.") (defprotocol SessionStore "An interface to a session storage engine. Implementing this protocol allows Ring session data to be stored in different places. Session keys are exposed to end users via a cookie, and therefore must be unguessable. A random UUID is a good choice for a session key. Session stores should come with a mechanism for expiring old session data." (read-session [store key] "Read a session map from the store. If the key is not found, nil is returned.") (write-session [store key data] "Write a session map to the store. Returns the (possibly changed) key under which the data was stored. If the key is nil, the session is considered to be new, and a fresh key should be generated.") (delete-session [store key] "Delete a session map from the store, and returns the session key. If the returned key is nil, the session cookie will be removed.")) ring-1.6.2/ring-core/src/ring/util/000077500000000000000000000000001313243611400170455ustar00rootroot00000000000000ring-1.6.2/ring-core/src/ring/util/io.clj000066400000000000000000000030261313243611400201470ustar00rootroot00000000000000(ns ring.util.io "Utility functions for handling I/O." (:require [clojure.java.io :as io]) (:import [java.io PipedInputStream PipedOutputStream ByteArrayInputStream File Closeable IOException])) (defn piped-input-stream "Create an input stream from a function that takes an output stream as its argument. The function will be executed in a separate thread. The stream will be automatically closed after the function finishes. For example: (piped-input-stream (fn [ostream] (spit ostream \"Hello\")))" {:added "1.1"} [func] (let [input (PipedInputStream.) output (PipedOutputStream.)] (.connect input output) (future (try (func output) (finally (.close output)))) input)) (defn string-input-stream "Returns a ByteArrayInputStream for the given String." {:added "1.1"} ([^String s] (ByteArrayInputStream. (.getBytes s))) ([^String s ^String encoding] (ByteArrayInputStream. (.getBytes s encoding)))) (defn close! "Ensure a stream is closed, swallowing any exceptions." {:added "1.2"} [stream] (when (instance? java.io.Closeable stream) (try (.close ^java.io.Closeable stream) (catch IOException _ nil)))) (defn last-modified-date "Returns the last modified date for a file, rounded down to the nearest second." {:added "1.2"} [^File file] (-> (.lastModified file) (/ 1000) (long) (* 1000) (java.util.Date.))) ring-1.6.2/ring-core/src/ring/util/mime_type.clj000066400000000000000000000070551313243611400215360ustar00rootroot00000000000000(ns ring.util.mime-type "Utility functions for determining the mime-types files." (:require [clojure.string :as str])) (def ^{:doc "A map of file extensions to mime-types."} default-mime-types {"7z" "application/x-7z-compressed" "aac" "audio/aac" "ai" "application/postscript" "appcache" "text/cache-manifest" "asc" "text/plain" "atom" "application/atom+xml" "avi" "video/x-msvideo" "bin" "application/octet-stream" "bmp" "image/bmp" "bz2" "application/x-bzip" "class" "application/octet-stream" "cer" "application/pkix-cert" "crl" "application/pkix-crl" "crt" "application/x-x509-ca-cert" "css" "text/css" "csv" "text/csv" "deb" "application/x-deb" "dart" "application/dart" "dll" "application/octet-stream" "dmg" "application/octet-stream" "dms" "application/octet-stream" "doc" "application/msword" "dvi" "application/x-dvi" "edn" "application/edn" "eot" "application/vnd.ms-fontobject" "eps" "application/postscript" "etx" "text/x-setext" "exe" "application/octet-stream" "flv" "video/x-flv" "flac" "audio/flac" "gif" "image/gif" "gz" "application/gzip" "htm" "text/html" "html" "text/html" "ico" "image/x-icon" "iso" "application/x-iso9660-image" "jar" "application/java-archive" "jpe" "image/jpeg" "jpeg" "image/jpeg" "jpg" "image/jpeg" "js" "text/javascript" "json" "application/json" "lha" "application/octet-stream" "lzh" "application/octet-stream" "mov" "video/quicktime" "m4v" "video/mp4" "mp3" "audio/mpeg" "mp4" "video/mp4" "mpe" "video/mpeg" "mpeg" "video/mpeg" "mpg" "video/mpeg" "oga" "audio/ogg" "ogg" "audio/ogg" "ogv" "video/ogg" "pbm" "image/x-portable-bitmap" "pdf" "application/pdf" "pgm" "image/x-portable-graymap" "png" "image/png" "pnm" "image/x-portable-anymap" "ppm" "image/x-portable-pixmap" "ppt" "application/vnd.ms-powerpoint" "ps" "application/postscript" "qt" "video/quicktime" "rar" "application/x-rar-compressed" "ras" "image/x-cmu-raster" "rb" "text/plain" "rd" "text/plain" "rss" "application/rss+xml" "rtf" "application/rtf" "sgm" "text/sgml" "sgml" "text/sgml" "svg" "image/svg+xml" "swf" "application/x-shockwave-flash" "tar" "application/x-tar" "tif" "image/tiff" "tiff" "image/tiff" "ttf" "application/x-font-ttf" "txt" "text/plain" "webm" "video/webm" "wmv" "video/x-ms-wmv" "woff" "application/font-woff" "xbm" "image/x-xbitmap" "xls" "application/vnd.ms-excel" "xml" "text/xml" "xpm" "image/x-xpixmap" "xwd" "image/x-xwindowdump" "zip" "application/zip"}) (defn- filename-ext "Returns the file extension of a filename or filepath." [filename] (if-let [ext (second (re-find #"\.([^./\\]+)$" filename))] (str/lower-case ext))) (defn ext-mime-type "Get the mimetype from the filename extension. Takes an optional map of extensions to mimetypes that overrides values in the default-mime-types map." ([filename] (ext-mime-type filename {})) ([filename mime-types] (let [mime-types (merge default-mime-types mime-types)] (mime-types (filename-ext filename))))) ring-1.6.2/ring-core/src/ring/util/parsing.clj000066400000000000000000000012531313243611400212030ustar00rootroot00000000000000(ns ring.util.parsing "Regular expressions for parsing HTTP. For internal use.") (def ^{:doc "HTTP token: 1*. See RFC2068" :added "1.3"} re-token #"[!#$%&'*\-+.0-9A-Z\^_`a-z\|~]+") (def ^{:doc "HTTP quoted-string: <\"> * <\">. See RFC2068." :added "1.3"} re-quoted #"\"(\\\"|[^\"])*\"") (def ^{:doc "HTTP value: token | quoted-string. See RFC2109" :added "1.3"} re-value (str re-token "|" re-quoted)) (def ^{:doc "Pattern for pulling the charset out of the content-type header" :added "1.6"} re-charset (re-pattern (str ";(?:.*\\s)?(?i:charset)=(" re-value ")\\s*(?:;|$)"))) ring-1.6.2/ring-core/src/ring/util/request.clj000066400000000000000000000047761313243611400212450ustar00rootroot00000000000000(ns ring.util.request "Functions for augmenting and pulling information from request maps." (:require [ring.util.parsing :refer [re-value]])) (defn request-url "Return the full URL of the request." {:added "1.2"} [request] (str (-> request :scheme name) "://" (get-in request [:headers "host"]) (:uri request) (if-let [query (:query-string request)] (str "?" query)))) (defn content-type "Return the content-type of the request, or nil if no content-type is set." {:added "1.3"} [request] (if-let [type (get-in request [:headers "content-type"])] (second (re-find #"^(.*?)(?:;|$)" type)))) (defn content-length "Return the content-length of the request, or nil no content-length is set." {:added "1.3"} [request] (if-let [^String length (get-in request [:headers "content-length"])] (Long. length))) (def ^:private charset-pattern (re-pattern (str ";(?:.*\\s)?(?i:charset)=(" re-value ")\\s*(?:;|$)"))) (defn character-encoding "Return the character encoding for the request, or nil if it is not set." {:added "1.3"} [request] (if-let [type (get-in request [:headers "content-type"])] (second (re-find charset-pattern type)))) (defn urlencoded-form? "True if a request contains a urlencoded form in the body." {:added "1.3"} [request] (if-let [^String type (content-type request)] (.startsWith type "application/x-www-form-urlencoded"))) (defmulti ^String body-string "Return the request body as a string." {:arglists '([request]), :added "1.2"} (comp class :body)) (defmethod body-string nil [_] nil) (defmethod body-string String [request] (:body request)) (defmethod body-string clojure.lang.ISeq [request] (apply str (:body request))) (defmethod body-string java.io.File [request] (slurp (:body request))) (defmethod body-string java.io.InputStream [request] (slurp (:body request))) (defn path-info "Returns the relative path of the request." {:added "1.2"} [request] (or (:path-info request) (:uri request))) (defn in-context? "Returns true if the URI of the request is a subpath of the supplied context." {:added "1.2"} [request context] (.startsWith ^String (:uri request) context)) (defn set-context "Associate a context and path-info with the request. The request URI must be a subpath of the supplied context." {:added "1.2"} [request ^String context] {:pre [(in-context? request context)]} (assoc request :context context :path-info (subs (:uri request) (.length context)))) ring-1.6.2/ring-core/src/ring/util/response.clj000066400000000000000000000247561313243611400214130ustar00rootroot00000000000000(ns ring.util.response "Functions for generating and augmenting response maps." (:require [clojure.java.io :as io] [clojure.string :as str] [ring.util.io :refer [last-modified-date]] [ring.util.parsing :refer [re-charset]] [ring.util.time :refer [format-date]]) (:import [java.io File] [java.util Date] [java.net URL URLDecoder URLEncoder])) (def ^{:added "1.4"} redirect-status-codes "Map a keyword to a redirect status code." {:moved-permanently 301 :found 302 :see-other 303 :temporary-redirect 307 :permanent-redirect 308}) (defn redirect "Returns a Ring response for an HTTP 302 redirect. Status may be a key in redirect-status-codes or a numeric code. Defaults to 302" ([url] (redirect url :found)) ([url status] {:status (redirect-status-codes status status) :headers {"Location" url} :body ""})) (defn redirect-after-post "Returns a Ring response for an HTTP 303 redirect. Deprecated in favor of using redirect with a :see-other status." {:deprecated "1.4"} [url] {:status 303 :headers {"Location" url} :body ""}) (defn created "Returns a Ring response for a HTTP 201 created response." {:added "1.2"} ([url] (created url nil)) ([url body] {:status 201 :headers {"Location" url} :body body})) (defn not-found "Returns a 404 'not found' response." {:added "1.1"} [body] {:status 404 :headers {} :body body}) (defn response "Returns a skeletal Ring response with the given body, status of 200, and no headers." [body] {:status 200 :headers {} :body body}) (defn status "Returns an updated Ring response with the given status." [resp status] (assoc resp :status status)) (defn header "Returns an updated Ring response with the specified header added." [resp name value] (assoc-in resp [:headers name] (str value))) (defn- canonical-path ^String [^File file] (str (.getCanonicalPath file) (if (.isDirectory file) File/separatorChar))) (defn- safe-path? [^String root ^String path] (.startsWith (canonical-path (File. root path)) (canonical-path (File. root)))) (defn- directory-transversal? "Check if a path contains '..'." [^String path] (-> (str/split path #"/|\\") (set) (contains? ".."))) (defn- find-file-named [^File dir ^String filename] (let [path (File. dir filename)] (if (.isFile path) path))) (defn- find-file-starting-with [^File dir ^String prefix] (first (filter #(.startsWith (.toLowerCase (.getName ^File %)) prefix) (.listFiles dir)))) (defn- find-index-file "Search the directory for an index file." [^File dir] (or (find-file-named dir "index.html") (find-file-named dir "index.htm") (find-file-starting-with dir "index."))) (defn- safely-find-file [^String path opts] (if-let [^String root (:root opts)] (if (or (safe-path? root path) (and (:allow-symlinks? opts) (not (directory-transversal? path)))) (File. root path)) (File. path))) (defn- find-file [^String path opts] (if-let [^File file (safely-find-file path opts)] (cond (.isDirectory file) (and (:index-files? opts true) (find-index-file file)) (.exists file) file))) (defn- file-data [^File file] {:content file :content-length (.length file) :last-modified (last-modified-date file)}) (defn- content-length [resp len] (if len (header resp "Content-Length" len) resp)) (defn- last-modified [resp last-mod] (if last-mod (header resp "Last-Modified" (format-date last-mod)) resp)) (defn file-response "Returns a Ring response to serve a static file, or nil if an appropriate file does not exist. Options: :root - take the filepath relative to this root path :index-files? - look for index.* files in directories (defaults to true) :allow-symlinks? - allow symlinks that lead to paths outside the root path (defaults to false)" ([filepath] (file-response filepath {})) ([filepath options] (if-let [file (find-file filepath options)] (let [data (file-data file)] (-> (response (:content data)) (content-length (:content-length data)) (last-modified (:last-modified data))))))) ;; In Clojure 1.5.1, the as-file function does not correctly decode ;; UTF-8 byte sequences. ;; ;; See: http://dev.clojure.org/jira/browse/CLJ-1177 ;; ;; As a work-around, we'll backport the fix from CLJ-1177 into ;; url-as-file. (defn- ^File url-as-file [^java.net.URL u] (-> (.getFile u) (str/replace \/ File/separatorChar) (str/replace "+" (URLEncoder/encode "+" "UTF-8")) (URLDecoder/decode "UTF-8") io/as-file)) (defn content-type "Returns an updated Ring response with the a Content-Type header corresponding to the given content-type." [resp content-type] (header resp "Content-Type" content-type)) (defn find-header "Looks up a header in a Ring response (or request) case insensitively, returning the header map entry, or nil if not present." {:added "1.4"} [resp ^String header-name] (->> (:headers resp) (filter #(.equalsIgnoreCase header-name (key %))) (first))) (defn get-header "Looks up a header in a Ring response (or request) case insensitively, returning the value of the header, or nil if not present." {:added "1.2"} [resp header-name] (some-> resp (find-header header-name) val)) (defn update-header "Looks up a header in a Ring response (or request) case insensitively, then updates the header with the supplied function and arguments in the manner of update-in." {:added "1.4"} [resp header-name f & args] (let [header-key (or (some-> resp (find-header header-name) key) header-name)] (update-in resp [:headers header-key] #(apply f % args)))) (defn charset "Returns an updated Ring response with the supplied charset added to the Content-Type header." {:added "1.1"} [resp charset] (update-header resp "Content-Type" (fn [content-type] (-> (or content-type "text/plain") (str/replace #";\s*charset=[^;]*" "") (str "; charset=" charset))))) (defn get-charset "Gets the character encoding of a Ring response." {:added "1.6"} [resp] (if-let [content-type (get-header resp "Content-Type")] (second (re-find re-charset content-type)))) (defn set-cookie "Sets a cookie on the response. Requires the handler to be wrapped in the wrap-cookies middleware." {:added "1.1"} [resp name value & [opts]] (assoc-in resp [:cookies name] (merge {:value value} opts))) (defn response? "True if the supplied value is a valid response map." {:added "1.1"} [resp] (and (map? resp) (integer? (:status resp)) (map? (:headers resp)))) (defmulti resource-data "Returns data about the resource specified by url, or nil if an appropriate resource does not exist. The return value is a map with optional values for: :content - the content of the URL, suitable for use as the :body of a ring response :content-length - the length of the :content, nil if not available :last-modified - the Date the :content was last modified, nil if not available This dispatches on the protocol of the URL as a keyword, and implementations are provided for :file and :jar. If you are on a platform where (Class/getResource) returns URLs with a different protocol, you will need to provide an implementation for that protocol. This function is used internally by url-response." {:arglists '([url]), :added "1.4"} (fn [^java.net.URL url] (keyword (.getProtocol url)))) (defmethod resource-data :file [url] (if-let [file (url-as-file url)] (if-not (.isDirectory file) (file-data file)))) (defn- add-ending-slash [^String path] (if (.endsWith path "/") path (str path "/"))) (defn- jar-directory? [^java.net.JarURLConnection conn] (let [jar-file (.getJarFile conn) entry-name (.getEntryName conn) dir-entry (.getEntry jar-file (add-ending-slash entry-name))] (and dir-entry (.isDirectory dir-entry)))) (defn- connection-content-length [^java.net.URLConnection conn] (let [len (.getContentLength conn)] (if (<= 0 len) len))) (defn- connection-last-modified [^java.net.URLConnection conn] (let [last-mod (.getLastModified conn)] (if-not (zero? last-mod) (Date. last-mod)))) (defmethod resource-data :jar [^java.net.URL url] (let [conn (.openConnection url)] (if-not (jar-directory? conn) {:content (.getInputStream conn) :content-length (connection-content-length conn) :last-modified (connection-last-modified conn)}))) (defn url-response "Return a response for the supplied URL." {:added "1.2"} [^URL url] (if-let [data (resource-data url)] (-> (response (:content data)) (content-length (:content-length data)) (last-modified (:last-modified data))))) (defn- get-resources [path ^ClassLoader loader] (-> (or loader (.getContextClassLoader (Thread/currentThread))) (.getResources path) (enumeration-seq))) (defn- safe-file-resource? [{:keys [body]} {:keys [root loader allow-symlinks?]}] (or allow-symlinks? (nil? root) (let [root (.replaceAll (str root) "^/" "")] (or (str/blank? root) (let [path (canonical-path body)] (some #(and (= "file" (.getProtocol ^URL %)) (.startsWith path (canonical-path (url-as-file %)))) (get-resources root loader))))))) (defn resource-response "Returns a Ring response to serve a packaged resource, or nil if the resource does not exist. Options: :root - take the resource relative to this root :loader - resolve the resource in this class loader :allow-symlinks? - allow symlinks that lead to paths outside the root classpath directories (defaults to false)" ([path] (resource-response path {})) ([path options] (let [path (-> (str "/" path) (.replace "//" "/")) root+path (-> (str (:root options) path) (.replaceAll "^/" "")) load #(if-let [loader (:loader options)] (io/resource % loader) (io/resource %))] (if-not (directory-transversal? root+path) (if-let [resource (load root+path)] (let [response (url-response resource)] (if (or (not (instance? File (:body response))) (safe-file-resource? response options)) response))))))) ring-1.6.2/ring-core/src/ring/util/test.clj000066400000000000000000000005551313243611400205230ustar00rootroot00000000000000(ns ring.util.test "Utilities for testing Ring components. All functions in this namespace are currently deprecated." {:deprecated "1.1"} (:require [ring.util.io :as io])) (def ^{:doc "Returns a ByteArrayInputStream for the given String. See: ring.util.io/string-input-stream." :deprecated "1.1"} string-input-stream io/string-input-stream) ring-1.6.2/ring-core/src/ring/util/time.clj000066400000000000000000000021071313243611400204750ustar00rootroot00000000000000(ns ring.util.time "Functions for dealing with time and dates in HTTP requests." (:require [clojure.string :as str]) (:import [java.text ParseException SimpleDateFormat] [java.util Locale TimeZone])) (def ^:no-doc http-date-formats {:rfc1123 "EEE, dd MMM yyyy HH:mm:ss zzz" :rfc1036 "EEEE, dd-MMM-yy HH:mm:ss zzz" :asctime "EEE MMM d HH:mm:ss yyyy"}) (defn- ^SimpleDateFormat formatter [format] (doto (SimpleDateFormat. ^String (http-date-formats format) Locale/US) (.setTimeZone (TimeZone/getTimeZone "GMT")))) (defn- attempt-parse [date format] (try (.parse (formatter format) date) (catch ParseException _ nil))) (defn- trim-quotes [s] (str/replace s #"^'|'$" "")) (defn parse-date "Attempt to parse a HTTP date. Returns nil if unsuccessful." {:added "1.2"} [http-date] (->> (keys http-date-formats) (map (partial attempt-parse (trim-quotes http-date))) (remove nil?) (first))) (defn format-date "Format a date as RFC1123 format." {:added "1.2"} [^java.util.Date date] (.format (formatter :rfc1123) date)) ring-1.6.2/ring-core/test/000077500000000000000000000000001313243611400153215ustar00rootroot00000000000000ring-1.6.2/ring-core/test/resource.jar000066400000000000000000000011541313243611400176470ustar00rootroot00000000000000PK6EMETA-INF/MANIFEST.MFMLK-. K-*ϳR03r*IJ ͍ %NV \E%)`̜R) N% `CBsxxPKT l\jPK! ring/assets/PKPK!ring/PKPK3Ering/assets/foo.htmlKJ,PK"\ PK6ET l\jMETA-INF/MANIFEST.MFPK! ring/assets/PK!ring/PK3E"\ ring/assets/foo.htmlPKaring-1.6.2/ring-core/test/ring/000077500000000000000000000000001313243611400162605ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/assets/000077500000000000000000000000001313243611400175625ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/assets/bars.txt000066400000000000000000000000051313243611400212450ustar00rootroot00000000000000bars ring-1.6.2/ring-core/test/ring/assets/bars/000077500000000000000000000000001313243611400205115ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/assets/bars/backlink000077700000000000000000000000001313243611400224222../ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/assets/bars/foo.html000066400000000000000000000000031313243611400221530ustar00rootroot00000000000000fooring-1.6.2/ring-core/test/ring/assets/foo.html000066400000000000000000000000031313243611400212240ustar00rootroot00000000000000fooring-1.6.2/ring-core/test/ring/assets/hello world.txt000066400000000000000000000000141313243611400225310ustar00rootroot00000000000000Hello World ring-1.6.2/ring-core/test/ring/assets/index.asp000066400000000000000000000000121313243611400213670ustar00rootroot00000000000000asp index ring-1.6.2/ring-core/test/ring/assets/index.html000066400000000000000000000000051313243611400215520ustar00rootroot00000000000000indexring-1.6.2/ring-core/test/ring/assets/index.js000066400000000000000000000000211313243611400212200ustar00rootroot00000000000000javascript index ring-1.6.2/ring-core/test/ring/assets/plain.txt000066400000000000000000000000061313243611400214220ustar00rootroot00000000000000plain ring-1.6.2/ring-core/test/ring/assets/random.xyz000066400000000000000000000000071313243611400216130ustar00rootroot00000000000000random ring-1.6.2/ring-core/test/ring/assets/test-resource.jar000066400000000000000000000012271313243611400230660ustar00rootroot00000000000000PK QSCpublic/UT 1bR1bRux PK QSCpublic/empty-resourceUT 1bR1bRux PK QSCzzopublic/hi-resourceUT 1bR1bRux hi PK QSCpublic/empty-dir/UT 1bR1bRux PK QSCApublic/UT1bRux PK QSCApublic/empty-resourceUT1bRux PK QSCzzopublic/hi-resourceUT1bRux PK QSCApublic/empty-dir/UT1bRux PKW*ring-1.6.2/ring-core/test/ring/core/000077500000000000000000000000001313243611400172105ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/core/test/000077500000000000000000000000001313243611400201675ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/core/test/protocols.clj000066400000000000000000000040241313243611400227050ustar00rootroot00000000000000(ns ring.core.test.protocols (:require [clojure.test :refer :all] [clojure.java.io :as io] [ring.core.protocols :refer :all])) (deftest test-write-body-defaults (testing "strings" (let [output (java.io.ByteArrayOutputStream.) response {:body "Hello World"}] (write-body-to-stream (:body response) response output) (is (= "Hello World" (.toString output))))) (testing "strings with encoding" (let [output (java.io.ByteArrayOutputStream.) response {:headers {"Content-Type" "text/plain; charset=UTF-16"} :body "Hello World"}] (write-body-to-stream (:body response) response output) (is (= "Hello World" (.toString output "UTF-16"))))) (testing "seqs" (let [output (java.io.ByteArrayOutputStream.) response {:body (list "Hello" " " "World")}] (write-body-to-stream (:body response) response output) (is (= "Hello World" (.toString output))))) (testing "seqs with encoding" (let [output (java.io.ByteArrayOutputStream.) response {:headers {"Content-Type" "text/plain; charset=UTF-16"} :body (list "Hello" " " "World")}] (write-body-to-stream (:body response) response output) (is (= "Hello World" (.toString output "UTF-16"))))) (testing "input streams" (let [output (java.io.ByteArrayOutputStream.) response {:body (io/input-stream (io/resource "ring/assets/hello world.txt"))}] (write-body-to-stream (:body response) response output) (is (= "Hello World\n" (.toString output))))) (testing "files" (let [output (java.io.ByteArrayOutputStream.) response {:body (io/file "test/ring/assets/hello world.txt")}] (write-body-to-stream (:body response) response output) (is (= "Hello World\n" (.toString output))))) (testing "nil" (let [output (java.io.ByteArrayOutputStream.) response {:body nil}] (write-body-to-stream (:body response) response output) (is (= "" (.toString output)))))) ring-1.6.2/ring-core/test/ring/middleware/000077500000000000000000000000001313243611400203755ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/middleware/multipart_params/000077500000000000000000000000001313243611400237615ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/middleware/multipart_params/test/000077500000000000000000000000001313243611400247405ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/middleware/multipart_params/test/byte_array.clj000066400000000000000000000011071313243611400275720ustar00rootroot00000000000000(ns ring.middleware.multipart-params.test.byte-array (:require [clojure.test :refer :all] [ring.middleware.multipart-params.byte-array :refer :all] [ring.util.io :refer [string-input-stream]])) (deftest test-byte-array-store (let [store (byte-array-store) result (store {:filename "foo.txt" :content-type "text/plain" :stream (string-input-stream "foo")})] (is (= (:filename result) "foo.txt")) (is (= (:content-type result) "text/plain")) (is (= (String. (:bytes result)) "foo")))) ring-1.6.2/ring-core/test/ring/middleware/multipart_params/test/request_context.clj000066400000000000000000000004111313243611400306620ustar00rootroot00000000000000(ns ring.middleware.multipart-params.test.request-context (:require [clojure.test :refer :all] [ring.middleware.multipart-params :as mp])) (deftest test-default-content-length (is (= -1 (.getContentLength (#'mp/request-context {} nil))))) ring-1.6.2/ring-core/test/ring/middleware/multipart_params/test/temp_file.clj000066400000000000000000000032441313243611400274010ustar00rootroot00000000000000(ns ring.middleware.multipart-params.test.temp-file (:require [clojure.test :refer :all] [ring.middleware.multipart-params.temp-file :refer :all] [ring.util.io :refer [string-input-stream]])) (deftest test-temp-file-store (let [store (temp-file-store) result (store {:filename "foo.txt" :content-type "text/plain" :stream (string-input-stream "foo")})] (is (= (:filename result) "foo.txt")) (is (= (:content-type result) "text/plain")) (is (= (:size result) 3)) (is (instance? java.io.File (:tempfile result))) (is (.exists (:tempfile result))) (is (= (slurp (:tempfile result)) "foo")))) (defn eventually [check n d] (loop [i n] (if (check) true (when (pos? i) (Thread/sleep d) (recur (dec i)))))) (deftest test-temp-file-expiry (let [store (temp-file-store {:expires-in 2}) result (store {:filename "foo.txt" :content-type "text/plain" :stream (string-input-stream "foo")})] (is (.exists (:tempfile result))) (Thread/sleep 2000) (let [deleted? (eventually #(not (.exists (:tempfile result))) 120 250)] (is deleted?)))) (defn all-threads [] (.keySet (Thread/getAllStackTraces))) (deftest test-temp-file-threads (let [threads0 (all-threads) store (temp-file-store) threads1 (all-threads)] (is (= (count threads0) (count threads1))) (dotimes [_ 200] (store {:filename "foo.txt" :content-type "text/plain" :stream (string-input-stream "foo")})) (is (< (count (all-threads)) 100)))) ring-1.6.2/ring-core/test/ring/middleware/session/000077500000000000000000000000001313243611400220605ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/middleware/session/test/000077500000000000000000000000001313243611400230375ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/middleware/session/test/cookie.clj000066400000000000000000000042611313243611400250050ustar00rootroot00000000000000(ns ring.middleware.session.test.cookie (:require [clojure.test :refer :all] [ring.middleware.session.store :refer :all] [ring.middleware.session.cookie :as cookie :refer [cookie-store]] [ring.util.codec :as codec] [crypto.random :as random])) (deftest cookie-session-read-not-exist (let [store (cookie-store)] (is (nil? (read-session store "non-existent"))))) (deftest cookie-session-create (let [store (cookie-store) sess-key (write-session store nil {:foo "bar"})] (is (not (nil? sess-key))) (is (= (read-session store sess-key) {:foo "bar"})))) (deftest cookie-session-update (let [store (cookie-store) sess-key (write-session store nil {:foo "bar"}) sess-key* (write-session store sess-key {:bar "baz"})] (is (not (nil? sess-key*))) (is (not= sess-key sess-key*)) (is (= (read-session store sess-key*) {:bar "baz"})))) (deftest cookie-session-delete (let [store (cookie-store) sess-key (write-session store nil {:foo "bar"}) sess-key* (delete-session store sess-key)] (is (not (nil? sess-key*))) (is (not= sess-key sess-key*)) (is (= (read-session store sess-key*) {})))) (defn seal-code-injection [key code] (let [data (#'cookie/encrypt key (.getBytes (str "#=" (pr-str code))))] (str (codec/base64-encode data) "--" (#'cookie/hmac key data)))) (deftest cookie-session-code-injection (let [secret-key (random/bytes 16) store (cookie-store {:key secret-key}) session (seal-code-injection secret-key `(+ 1 1))] (is (thrown? Exception (read-session store session))))) (deftest cookie-session-keyword-injection (let [store (cookie-store) bad-data {:foo 1 (keyword "bar 3 :baz ") 2}] (is (thrown? AssertionError (write-session store nil bad-data))))) (deftest cookie-session-invalid-key (is (thrown-with-msg? AssertionError #"the secret key must be exactly 16 bytes" (cookie-store {:key (.getBytes "abcd")}))) (is (thrown-with-msg? AssertionError #"the secret key must be exactly 16 bytes" (cookie-store {:key (.getBytes "012345678901234567890")})))) ring-1.6.2/ring-core/test/ring/middleware/session/test/memory.clj000066400000000000000000000025261313243611400250460ustar00rootroot00000000000000(ns ring.middleware.session.test.memory (:require [clojure.test :refer :all] [ring.middleware.session.store :refer :all] [ring.middleware.session.memory :refer :all])) (deftest memory-session-read-not-exist (let [store (memory-store)] (is (nil? (read-session store "non-existent"))))) (deftest memory-session-create (let [store (memory-store) sess-key (write-session store nil {:foo "bar"})] (is (not (nil? sess-key))) (is (= (read-session store sess-key) {:foo "bar"})))) (deftest memory-session-update (let [store (memory-store) sess-key (write-session store nil {:foo "bar"}) sess-key* (write-session store sess-key {:bar "baz"})] (is (= sess-key sess-key*)) (is (= (read-session store sess-key) {:bar "baz"})))) (deftest memory-session-delete (let [store (memory-store) sess-key (write-session store nil {:foo "bar"})] (is (nil? (delete-session store sess-key))) (is (nil? (read-session store sess-key))))) (deftest memory-session-custom-atom (let [session (atom {}) store (memory-store session) sess-key (write-session store nil {:foo "bar"})] (is (= (@session sess-key) {:foo "bar"})) (swap! session assoc sess-key {:foo "baz"}) (is (= (read-session store sess-key) {:foo "baz"})))) ring-1.6.2/ring-core/test/ring/middleware/test/000077500000000000000000000000001313243611400213545ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/middleware/test/content_type.clj000066400000000000000000000052511313243611400245640ustar00rootroot00000000000000(ns ring.middleware.test.content-type (:require [clojure.test :refer :all] [ring.middleware.content-type :refer :all])) (deftest wrap-content-type-test (testing "response without content-type" (let [response {:headers {}} handler (wrap-content-type (constantly response))] (is (= (handler {:uri "/foo/bar.png"}) {:headers {"Content-Type" "image/png"}})) (is (= (handler {:uri "/foo/bar.txt"}) {:headers {"Content-Type" "text/plain"}})))) (testing "response with content-type" (let [response {:headers {"Content-Type" "application/x-foo"}} handler (wrap-content-type (constantly response))] (is (= (handler {:uri "/foo/bar.png"}) {:headers {"Content-Type" "application/x-foo"}})))) (testing "unknown file extension" (let [response {:headers {}} handler (wrap-content-type (constantly response))] (is (= (handler {:uri "/foo/bar.xxxaaa"}) {:headers {"Content-Type" "application/octet-stream"}})) (is (= (handler {:uri "/foo/bar"}) {:headers {"Content-Type" "application/octet-stream"}})))) (testing "response with mime-types option" (let [response {:headers {}} handler (wrap-content-type (constantly response) {:mime-types {"edn" "application/edn"}})] (is (= (handler {:uri "/all.edn"}) {:headers {"Content-Type" "application/edn"}})))) (testing "nil response" (let [handler (wrap-content-type (constantly nil))] (is (nil? (handler {:uri "/foo/bar.txt"}))))) (testing "response header case insensitivity" (let [response {:headers {"CoNteNt-typE" "application/x-overridden"}} handler (wrap-content-type (constantly response))] (is (= (handler {:uri "/foo/bar.png"}) {:headers {"CoNteNt-typE" "application/x-overridden"}}))))) (deftest wrap-content-type-cps-test (testing "response without content-type" (let [handler (wrap-content-type (fn [_ respond _] (respond {:headers {}}))) response (promise) exception (promise)] (handler {:uri "/foo/bar.png"} response exception) (is (= @response {:headers {"Content-Type" "image/png"}})) (is (not (realized? exception))))) (testing "nil response" (let [handler (wrap-content-type (fn [_ respond _] (respond nil))) response (promise) exception (promise)] (handler {:uri "/foo/bar.png"} response exception) (is (nil? @response)) (is (not (realized? exception)))))) (deftest content-type-response-test (testing "function exists" (is (fn? content-type-response))) (testing "nil response" (is (nil? (content-type-response nil {}))))) ring-1.6.2/ring-core/test/ring/middleware/test/cookies.clj000066400000000000000000000226311313243611400235060ustar00rootroot00000000000000(ns ring.middleware.test.cookies (:require [clojure.test :refer :all] [clojure.string :as str] [ring.middleware.cookies :refer :all] [clj-time.core :refer [date-time interval]])) (deftest wrap-cookies-basic-cookie (let [req {:headers {"cookie" "a=b"}} resp ((wrap-cookies :cookies) req)] (is (= {"a" {:value "b"}} resp)))) (deftest wrap-cookies-multiple-cookies (let [req {:headers {"cookie" "a=b; c=d,e=f"}} resp ((wrap-cookies :cookies) req)] (is (= {"a" {:value "b"}, "c" {:value "d"}, "e" {:value "f"}} resp)))) (deftest wrap-cookies-quoted-cookies (let [req {:headers {"cookie" "a=\"b\""}} resp ((wrap-cookies :cookies) req)] (is (= {"a" {:value "b"}} resp)))) (deftest wrap-cookies-quoted-cookies-no-urlencode (let [req {:headers {"cookie" "a=U3VwIFdpenpvcmxkCg%3D%3D"}} resp ((wrap-cookies :cookies {:decoder identity}) req)] (is (= {"a" {:value "U3VwIFdpenpvcmxkCg%3D%3D"}} resp)))) (deftest wrap-cookies-set-basic-cookie (let [handler (constantly {:cookies {"a" "b"}}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" (list "a=b")} (:headers resp))))) (deftest wrap-cookies-set-multiple-cookies (let [handler (constantly {:cookies {"a" "b", "c" "d"}}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" (list "a=b" "c=d")} (:headers resp))))) (deftest wrap-cookies-set-keyword-cookie (let [handler (constantly {:cookies {:a "b"}}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" (list "a=b")} (:headers resp))))) (defn- split-set-cookie [headers] (letfn [(split-header [v] (set (mapcat #(str/split % #";") v)))] (update-in headers ["Set-Cookie"] split-header))) (deftest wrap-cookies-set-extra-attrs (let [cookies {"a" {:value "b", :path "/", :secure true, :http-only true }} handler (constantly {:cookies cookies}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" #{"a=b" "Path=/" "Secure" "HttpOnly"}} (split-set-cookie (:headers resp)))))) (deftest wrap-cookies-always-assocs-map (let [req {:headers {}} resp ((wrap-cookies :cookies) req)] (is (= {} resp)))) (deftest wrap-cookies-read-urlencoded (let [req {:headers {"cookie" "a=hello+world"}} resp ((wrap-cookies :cookies) req)] (is (= {"a" {:value "hello world"}} resp)))) (deftest wrap-cookies-no-urlencode (let [req {:headers {"cookie" "a=hello+world"}} resp ((wrap-cookies :cookies {:decoder identity}) req)] (is (= {"a" {:value "hello+world"}} resp)))) (deftest wrap-cookies-no-urlencode-base64 (let [req {:headers {"cookie" "a=U3VwIFdpenpvcmxkCg=="}} resp ((wrap-cookies :cookies {:decoder identity}) req)] (is (= {"a" {:value "U3VwIFdpenpvcmxkCg=="}} resp)))) (deftest wrap-cookies-set-urlencoded-cookie (let [handler (constantly {:cookies {"a" "hello world"}}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" (list "a=hello+world")} (:headers resp))))) (deftest wrap-cookies-set-no-urlencoded-cookie (let [handler (constantly {:cookies {"a" "hello world"}}) resp ((wrap-cookies handler {:encoder (fn [m] (apply format "%s=%s" (first (seq m))))}) {})] (is (= {"Set-Cookie" (list "a=hello world")} (:headers resp))))) (deftest wrap-cookies-invalid-url-encoded (let [req {:headers {"cookie" "a=%D"}} resp ((wrap-cookies :cookies) req)] (is (= {} resp)))) (deftest wrap-cookies-keep-set-cookies-intact (let [handler (constantly {:headers {"Set-Cookie" (list "a=b")} :cookies {:c "d"}}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" (list "a=b" "c=d")} (:headers resp))))) (deftest wrap-cookies-invalid-attrs (let [response {:cookies {"a" {:value "foo" :invalid true}}} handler (wrap-cookies (constantly response))] (is (thrown? AssertionError (handler {}))))) (deftest wrap-cookies-accepts-max-age (let [cookies {"a" {:value "b", :path "/", :secure true, :http-only true, :max-age 123}} handler (constantly {:cookies cookies}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" #{"a=b" "Path=/" "Secure" "HttpOnly" "Max-Age=123"}} (split-set-cookie (:headers resp)))))) (deftest wrap-cookies-accepts-same-site (testing "Allows :strict and :lax values" (let [cookies {"a" {:value "b" :path "/" :same-site :strict}} handler (constantly {:cookies cookies}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" #{"a=b" "Path=/" "SameSite=Strict"}} (split-set-cookie (:headers resp))))) (let [cookies {"a" {:value "b" :path "/" :same-site :lax}} handler (constantly {:cookies cookies}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" #{"a=b" "Path=/" "SameSite=Lax"}} (split-set-cookie (:headers resp)))))) (testing "Disallows other values" (let [cookies {"a" {:value "b" :path "/" :same-site :loose}} handler (constantly {:cookies cookies})] (is (thrown? AssertionError ((wrap-cookies handler) {})))))) (deftest wrap-cookies-accepts-expires (let [cookies {"a" {:value "b", :path "/", :secure true, :http-only true, :expires "123"}} handler (constantly {:cookies cookies}) resp ((wrap-cookies handler) {})] (is (= {"Set-Cookie" #{"a=b" "Path=/" "Secure" "HttpOnly" "Expires=123"}} (split-set-cookie (:headers resp)))))) (deftest wrap-cookies-accepts-max-age-from-clj-time (let [cookies {"a" {:value "b", :path "/", :secure true, :http-only true, :max-age (interval (date-time 2012) (date-time 2015))}} handler (constantly {:cookies cookies}) resp ((wrap-cookies handler) {}) max-age 94694400] (is (= {"Set-Cookie" #{"a=b" "Path=/" "Secure" "HttpOnly" (str "Max-Age=" max-age)}} (split-set-cookie (:headers resp)))))) (deftest wrap-cookies-accepts-expires-from-clj-time (let [cookies {"a" {:value "b", :path "/", :secure true, :http-only true, :expires (date-time 2015 12 31)}} handler (constantly {:cookies cookies}) resp ((wrap-cookies handler) {}) expires "Thu, 31 Dec 2015 00:00:00 +0000"] (is (= {"Set-Cookie" #{"a=b" "Path=/" "Secure" "HttpOnly" (str "Expires=" expires)}} (split-set-cookie (:headers resp)))))) (deftest wrap-cookies-accepts-expires-from-clj-time-in-non-us-locale (let [default-locale (java.util.Locale/getDefault)] (try (java.util.Locale/setDefault java.util.Locale/FRANCE) (let [cookies {"a" {:value "b", :path "/", :secure true, :http-only true, :expires (date-time 2015 12 31)}} handler (constantly {:cookies cookies}) resp ((wrap-cookies handler) {}) expires "Thu, 31 Dec 2015 00:00:00 +0000"] (is (= {"Set-Cookie" #{"a=b" "Path=/" "Secure" "HttpOnly" (str "Expires=" expires)}} (split-set-cookie (:headers resp))))) (finally (java.util.Locale/setDefault default-locale))))) (deftest wrap-cookies-throws-exception-when-not-using-intervals-correctly (let [cookies {"a" {:value "b", :path "/", :secure true, :http-only true, :expires (interval (date-time 2012) (date-time 2015))}} handler (constantly {:cookies cookies})] (is (thrown? AssertionError ((wrap-cookies handler) {}))))) (deftest wrap-cookies-throws-exception-when-not-using-datetime-correctly (let [cookies {"a" {:value "b", :path "/", :secure true, :http-only true, :max-age (date-time 2015 12 31)}} handler (constantly {:cookies cookies})] (is (thrown? AssertionError ((wrap-cookies handler) {}))))) (deftest parse-cookies-on-request-basic-cookie (let [req {:headers {"cookie" "a=b"}}] (is (= {"a" {:value "b"}} ((cookies-request req) :cookies))))) (deftest parse-cookies-on-request-multiple-cookies (let [req {:headers {"cookie" "a=b; c=d,e=f"}}] (is (= {"a" {:value "b"}, "c" {:value "d"}, "e" {:value "f"}} ((cookies-request req) :cookies))))) (deftest parse-cookies-url-encoding (let [req {:headers {"cookie" "a=%22/test%22"}}] (is (= {"a" {:value "\"/test\""}} ((cookies-request req) :cookies))))) (deftest wrap-cookies-cps-test (testing "read cookie" (let [handler (wrap-cookies (fn [req respond _] (respond (:cookies req)))) request {:headers {"cookie" "a=b"}} response (promise) exception (promise)] (handler request response exception) (is (= {"a" {:value "b"}} @response)) (is (not (realized? exception))))) (testing "write cookie" (let [handler (wrap-cookies (fn [_ respond _] (respond {:cookies {"a" "b"}}))) response (promise) exception (promise)] (handler {} response exception) (is (= {"Set-Cookie" (list "a=b")} (:headers @response))) (is (not (realized? exception)))))) (deftest cookies-response-test (is (fn? cookies-response))) (deftest cookies-request-test (is (fn? cookies-request))) ring-1.6.2/ring-core/test/ring/middleware/test/file.clj000066400000000000000000000070661313243611400227760ustar00rootroot00000000000000(ns ring.middleware.test.file (:require [clojure.test :refer :all] [ring.middleware.file :refer :all]) (:import [java.io File])) (deftest wrap-file-no-directory (is (thrown-with-msg? Exception #".*Directory does not exist.*" (wrap-file (constantly :response) "not_here")))) (def public-dir "test/ring/assets") (def index-html (File. ^String public-dir "index.html")) (def foo-html (File. ^String public-dir "foo.html")) (def app (wrap-file (constantly :response) public-dir)) (deftest test-wrap-file-unsafe-method (is (= :response (app {:request-method :post :uri "/foo"})))) (deftest test-wrap-file-forbidden-url (is (= :response (app {:request-method :get :uri "/../foo"})))) (deftest test-wrap-file-no-file (is (= :response (app {:request-method :get :uri "/dynamic"})))) (deftest test-wrap-file-directory (let [{:keys [status headers body]} (app {:request-method :get :uri "/"})] (is (= 200 status)) (is (= (into #{} (keys headers)) #{"Content-Length" "Last-Modified"})) (is (= index-html body)))) (deftest test-wrap-file-with-extension (let [{:keys [status headers body]} (app {:request-method :get :uri "/foo.html"})] (is (= 200 status)) (is (= (into #{} (keys headers)) #{"Content-Length" "Last-Modified"})) (is (= foo-html body)))) (deftest test-wrap-file-no-index (let [app (wrap-file (constantly :response) public-dir {:index-files? false}) resp (app {:request-method :get :uri "/"})] (is (= :response resp)))) (deftest test-wrap-file-path-info (let [request {:request-method :get :uri "/bar/foo.html" :context "/bar" :path-info "/foo.html"} {:keys [status headers body]} (app request)] (is (= 200 status)) (is (= (into #{} (keys headers)) #{"Content-Length" "Last-Modified"})) (is (= foo-html body)))) (deftest wrap-file-cps-test (let [dynamic-response {:status 200, :headers {}, :body "foo"} handler (-> (fn [_ respond _] (respond dynamic-response)) (wrap-file public-dir))] (testing "file response" (let [request {:request-method :get :uri "/foo.html"} response (promise) exception (promise)] (handler request response exception) (is (= 200 (:status @response))) (is (= #{"Content-Length" "Last-Modified"} (-> @response :headers keys set))) (is (= foo-html (:body @response))) (is (not (realized? exception))))) (testing "non-file response" (let [request {:request-method :get :uri "/dynamic"} response (promise) exception (promise)] (handler request response exception) (is (= dynamic-response @response)) (is (not (realized? exception))))))) (deftest file-request-test (is (fn? file-request))) (deftest test-head-request (let [{:keys [status headers body]} (app {:request-method :head :uri "/foo.html"})] (is (= 200 status)) (is (= (into #{} (keys headers)) #{"Content-Length" "Last-Modified"})) (is (nil? body)))) (deftest test-wrap-file-with-java-io-file (let [app (wrap-file (constantly :response) (File. public-dir))] (let [{:keys [status headers body]} (app {:request-method :get :uri "/"})] (is (= 200 status)) (is (= (into #{} (keys headers)) #{"Content-Length" "Last-Modified"})) (is (= index-html body))) (let [{:keys [status headers body]} (app {:request-method :get :uri "/foo.html"})] (is (= 200 status)) (is (= (into #{} (keys headers)) #{"Content-Length" "Last-Modified"})) (is (= foo-html body))))) ring-1.6.2/ring-core/test/ring/middleware/test/file_info.clj000066400000000000000000000076261313243611400240130ustar00rootroot00000000000000(ns ring.middleware.test.file-info (:require [clojure.test :refer :all] [ring.middleware.file-info :refer :all]) (:import [java.io File])) (def non-file-app (wrap-file-info (constantly {:headers {} :body "body"}))) (def known-file (File. "test/ring/assets/plain.txt")) (def known-file-app (wrap-file-info (constantly {:headers {} :body known-file}))) (def unknown-file (File. "test/ring/assets/random.xyz")) (def unknown-file-app (wrap-file-info (constantly {:headers {} :body unknown-file}))) (defmacro with-last-modified "Lets us use a known file modification time for tests, without permanently changing the file's modification time." [[file new-time] form] `(let [old-time# (.lastModified ~file)] (.setLastModified ~file ~(* new-time 1000)) (try ~form (finally (.setLastModified ~file old-time#))))) (def custom-type-app (wrap-file-info (constantly {:headers {} :body known-file}) {"txt" "custom/type"})) (deftest wrap-file-info-non-file-response (is (= {:headers {} :body "body"} (non-file-app {})))) (deftest wrap-file-info-known-file-response (with-last-modified [known-file 1263506400] (is (= {:headers {"Content-Type" "text/plain" "Content-Length" "6" "Last-Modified" "Thu, 14 Jan 2010 22:00:00 +0000"} :body known-file} (known-file-app {:headers {}}))))) (deftest wrap-file-info-unknown-file-response (with-last-modified [unknown-file 1263506400] (is (= {:headers {"Content-Type" "application/octet-stream" "Content-Length" "7" "Last-Modified" "Thu, 14 Jan 2010 22:00:00 +0000"} :body unknown-file} (unknown-file-app {:headers {}}))))) (deftest wrap-file-info-custom-mime-types (with-last-modified [known-file 0] (is (= {:headers {"Content-Type" "custom/type" "Content-Length" "6" "Last-Modified" "Thu, 01 Jan 1970 00:00:00 +0000"} :body known-file} (custom-type-app {:headers {}}))))) (deftest wrap-file-info-if-modified-since-hit (with-last-modified [known-file 1263506400] (is (= {:status 304 :headers {"Content-Type" "text/plain" "Content-Length" "0" "Last-Modified" "Thu, 14 Jan 2010 22:00:00 +0000"} :body ""} (known-file-app {:headers {"if-modified-since" "Thu, 14 Jan 2010 22:00:00 +0000" }}))))) (deftest wrap-file-info-if-modified-miss (with-last-modified [known-file 1263506400] (is (= {:headers {"Content-Type" "text/plain" "Content-Length" "6" "Last-Modified" "Thu, 14 Jan 2010 22:00:00 +0000"} :body known-file} (known-file-app {:headers {"if-modified-since" "Wed, 13 Jan 2010 22:00:00 +0000"}}))))) (deftest wrap-file-info-cps-test (testing "file response" (with-last-modified [known-file 1263506400] (let [handler (wrap-file-info (fn [_ respond _] (respond {:headers {} :body known-file}))) response (promise) exception (promise)] (handler {:headers {}} response exception) (is (= {:headers {"Content-Type" "text/plain" "Content-Length" "6" "Last-Modified" "Thu, 14 Jan 2010 22:00:00 +0000"} :body known-file} @response)) (is (not (realized? exception)))))) (testing "non-file response" (let [handler (wrap-file-info (fn [_ respond _] (respond {:headers {} :body "body"}))) response (promise) exception (promise)] (handler {:headers {}} response exception) (is (= {:headers {} :body "body"} @response)) (is (not (realized? exception)))))) (deftest file-info-response-test (is (fn? file-info-response))) ring-1.6.2/ring-core/test/ring/middleware/test/flash.clj000066400000000000000000000053711313243611400231510ustar00rootroot00000000000000(ns ring.middleware.test.flash (:require [clojure.test :refer :all] [ring.middleware.flash :refer :all])) (deftest flash-is-added-to-session (let [message {:error "Could not save"} handler (wrap-flash (constantly {:flash message})) response (handler {:session {}})] (is (= (:session response) {:_flash message})))) (deftest flash-is-retrieved-from-session (let [message {:error "Could not save"} handler (wrap-flash (fn [request] (is (= (:flash request) message)) {}))] (handler {:session {:_flash message}}))) (deftest flash-is-removed-after-read (let [message {:error "Could not save"} handler (wrap-flash (constantly {:session {:foo "bar"}})) response (handler {:session {:_flash message}})] (is (nil? (:flash response))) (is (= (:session response) {:foo "bar"})))) (deftest flash-is-removed-after-read-not-touching-session-explicitly (let [message {:error "Could not save"} handler (wrap-flash (constantly {:status 200})) response (handler {:session {:_flash message :foo "bar"}})] (is (nil? (:flash response))) (is (= (:session response) {:foo "bar"})))) (deftest flash-doesnt-wipe-session (let [message {:error "Could not save"} handler (wrap-flash (constantly {:flash message})) response (handler {:session {:foo "bar"}})] (is (= (:session response) {:foo "bar", :_flash message})))) (deftest flash-overwrites-nil-session (let [message {:error "Could not save"} handler (wrap-flash (constantly {:flash message, :session nil})) response (handler {:session {:foo "bar"}})] (is (= (:session response) {:_flash message})))) (deftest flash-not-except-on-nil-response (let [handler (wrap-flash (constantly nil))] (is (nil? (handler {}))))) (deftest wrap-flash-cps-test (testing "flash added to session" (let [message {:error "Could not save"} handler (wrap-flash (fn [_ respond _] (respond {:flash message}))) response (promise) exception (promise)] (handler {:session {}} response exception) (is (= (:session @response) {:_flash message})) (is (not (realized? exception))))) (testing "flash retrieved from session" (let [message {:error "Could not save"} handler (wrap-flash (fn [req respond _] (respond {:result (:flash req)}))) response (promise) exception (promise)] (handler {:session {:_flash message}} response exception) (is (= (:result @response) message)) (is (not (realized? exception)))))) (deftest flash-request-test (is (fn? flash-request))) (deftest flash-response-test (is (fn? flash-response)) (is (nil? (flash-response nil {})))) ring-1.6.2/ring-core/test/ring/middleware/test/head.clj000066400000000000000000000027161313243611400227550ustar00rootroot00000000000000(ns ring.middleware.test.head (:require [clojure.test :refer :all] [ring.middleware.head :refer :all])) (defn- handler ([req] {:status 200 :headers {"X-method" (name (:request-method req))} :body "Foobar"}) ([req cont raise] (cont {:status 200 :headers {"X-method" (name (:request-method req))} :body "Foobar"}))) (deftest test-wrap-head (let [resp ((wrap-head handler) {:request-method :head})] (is (nil? (:body resp))) (is (= "get" (get-in resp [:headers "X-method"])))) (let [resp ((wrap-head handler) {:request-method :post})] (is (= (:body resp) "Foobar")) (is (= "post" (get-in resp [:headers "X-method"]))))) (deftest wrap-head-cps-test (testing "HEAD request" (let [response (promise) exception (promise)] ((wrap-head handler) {:request-method :head} response exception) (is (nil? (:body @response))) (is (= "get" (get-in @response [:headers "X-method"]))) (is (not (realized? exception))))) (testing "POST request" (let [response (promise) exception (promise)] ((wrap-head handler) {:request-method :post} response exception) (is (= "Foobar" (:body @response))) (is (= "post" (get-in @response [:headers "X-method"]))) (is (not (realized? exception)))))) (deftest head-request-test (is (fn? head-request))) (deftest head-response-test (is (fn? head-response)) (is (nil? (head-response nil {:request-method :head})))) ring-1.6.2/ring-core/test/ring/middleware/test/keyword_params.clj000066400000000000000000000022021313243611400250710ustar00rootroot00000000000000(ns ring.middleware.test.keyword-params (:require [clojure.test :refer :all] [ring.middleware.keyword-params :refer :all])) (def wrapped-echo (wrap-keyword-params identity)) (deftest test-wrap-keyword-params (are [in out] (= out (:params (wrapped-echo {:params in}))) {"foo" "bar" "biz" "bat"} {:foo "bar" :biz "bat"} {"foo" "bar" "biz" [{"bat" "one"} {"bat" "two"}]} {:foo "bar" :biz [{:bat "one"} {:bat "two"}]} {"foo" 1} {:foo 1} {"foo" 1 "1bar" 2 "baz*" 3 "quz-buz" 4 "biz.bang" 5} {:foo 1 "1bar" 2 :baz* 3 :quz-buz 4 "biz.bang" 5} {:foo "bar"} {:foo "bar"} {"foo" {:bar "baz"}} {:foo {:bar "baz"}} {"fòö" "bar" "bíz" "bat"} {:fòö "bar" :bíz "bat"})) (deftest wrap-keyword-params-cps-test (let [handler (wrap-keyword-params (fn [req respond _] (respond (:params req)))) response (promise) exception (promise)] (handler {:params {"foo" "bar" :baz "quz"}} response exception) (is (= {:foo "bar" :baz "quz"} @response)) (is (not (realized? exception))))) (deftest keyword-params-request-test (is (fn? keyword-params-request))) ring-1.6.2/ring-core/test/ring/middleware/test/multipart_params.clj000066400000000000000000000164301313243611400254360ustar00rootroot00000000000000(ns ring.middleware.test.multipart-params (:require [clojure.test :refer :all] [ring.middleware.multipart-params :refer :all] [ring.util.io :refer [string-input-stream]]) (:import [java.io InputStream])) (defn string-store [item] (-> (select-keys item [:filename :content-type]) (assoc :content (slurp (:stream item))))) (deftest test-wrap-multipart-params (let [form-body (str "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"upload\"; filename=\"test.txt\"\r\n" "Content-Type: text/plain\r\n\r\n" "foo\r\n" "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"baz\"\r\n\r\n" "qux\r\n" "--XXXX--") handler (wrap-multipart-params identity {:store string-store}) request {:headers {"content-type" "multipart/form-data; boundary=XXXX" "content-length" (str (count form-body))} :params {"foo" "bar"} :body (string-input-stream form-body)} response (handler request)] (is (= (get-in response [:params "foo"]) "bar")) (is (= (get-in response [:params "baz"]) "qux")) (let [upload (get-in response [:params "upload"])] (is (= (:filename upload) "test.txt")) (is (= (:content-type upload) "text/plain")) (is (= (:content upload) "foo"))))) (deftest test-multiple-params (let [form-body (str "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"foo\"\r\n\r\n" "bar\r\n" "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"foo\"\r\n\r\n" "baz\r\n" "--XXXX--") handler (wrap-multipart-params identity {:store string-store}) request {:headers {"content-type" "multipart/form-data; boundary=XXXX" "content-length" (str (count form-body))} :body (string-input-stream form-body)} response (handler request)] (is (= (get-in response [:params "foo"]) ["bar" "baz"])))) (defn all-threads [] (.keySet (Thread/getAllStackTraces))) (deftest test-multipart-threads (testing "no thread leakage when handler called" (let [handler (wrap-multipart-params identity)] (dotimes [_ 200] (handler {})) (is (< (count (all-threads)) 100)))) (testing "no thread leakage from default store" (let [form-body (str "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"upload\"; filename=\"test.txt\"\r\n" "Content-Type: text/plain\r\n\r\n" "foo\r\n" "--XXXX--")] (dotimes [_ 200] (let [handler (wrap-multipart-params identity) request {:headers {"content-type" "multipart/form-data; boundary=XXXX" "content-length" (str (count form-body))} :body (string-input-stream form-body)}] (handler request)))) (is (< (count (all-threads)) 100)))) (deftest wrap-multipart-params-cps-test (let [handler (wrap-multipart-params (fn [req respond _] (respond req))) form-body (str "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"foo\"\r\n\r\n" "bar\r\n" "--XXXX--") request {:headers {"content-type" "multipart/form-data; boundary=XXXX"} :body (string-input-stream form-body "UTF-8")} response (promise) exception (promise)] (handler request response exception) (is (= (get-in @response [:multipart-params "foo"]) "bar")) (is (not (realized? exception))))) (deftest multipart-params-request-test (is (fn? multipart-params-request))) (deftest decode-with-utf8-by-default (let [form-body (str "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"foo\"\r\n\r\n" "Øæß箣èé\r\n" "--XXXX--") request {:headers {"content-type" (str "multipart/form-data; boundary=XXXX")} :body (string-input-stream form-body "UTF-8")} request* (multipart-params-request request)] (is (= (get-in request* [:multipart-params "foo"]) "Øæß箣èé")))) (deftest parts-may-have-invidual-charsets-in-content-type (let [form-body (str "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"foo\"\r\n" "Content-Type: text/plain; charset=ISO-8859-15\r\n\r\n" "äÄÖöÅå€\r\n" "--XXXX--") request {:headers {"content-type" (str "multipart/form-data; boundary=XXXX")} :body (string-input-stream form-body "ISO-8859-15")} request* (multipart-params-request request)] (is (= (get-in request* [:multipart-params "foo"]) "äÄÖöÅå€")))) (deftest charset-may-be-defined-html5-style-parameter (let [form-body (str "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"foo\"\r\n\r\n" "Øæß箣èé\r\n" "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"_charset_\"\r\n\r\n" "UTF-8\r\n" "--XXXX--") request {:headers {"content-type" (str "multipart/form-data; boundary=XXXX; charset=US-ASCII")} :body (string-input-stream form-body "UTF-8")} request* (multipart-params-request request)] (is (= (get-in request* [:multipart-params "foo"]) "Øæß箣èé")))) (deftest forced-encoding-option-works (let [form-body (str "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"foo\"\r\n" "Content-Type: application/json; charset=UTF-8\r\n\r\n" "{\"åå\":\"ÄÖ\"}\r\n" "--XXXX--") request {:headers {"content-type" (str "multipart/form-data; boundary=XXXX; charset=US-ASCII")} :body (string-input-stream form-body "ISO-8859-1")} request* (multipart-params-request request {:encoding "ISO-8859-1"})] (is (= (get-in request* [:multipart-params "foo"]) "{\"åå\":\"ÄÖ\"}")))) (deftest fallback-encoding-option-works (let [form-body (str "--XXXX\r\n" "Content-Disposition: form-data;" "name=\"foo\"\r\n\r\n" "äÄÖöÅå€\r\n" "--XXXX--") request {:headers {"content-type" (str "multipart/form-data; boundary=XXXX; charset=US-ASCII")} :body (string-input-stream form-body "ISO-8859-15")} request* (multipart-params-request request {:fallback-encoding "ISO-8859-15"})] (is (= (get-in request* [:multipart-params "foo"]) "äÄÖöÅå€")))) ring-1.6.2/ring-core/test/ring/middleware/test/nested_params.clj000066400000000000000000000063361313243611400247030ustar00rootroot00000000000000(ns ring.middleware.test.nested-params (:require [clojure.test :refer :all] [clojure.string :as string] [ring.middleware.nested-params :refer :all])) (deftest nested-params-test (let [handler (wrap-nested-params :params)] (testing "nested parameter maps" (are [p r] (= (handler {:params p}) r) {"foo" "bar"} {"foo" "bar"} {"x[y]" "z"} {"x" {"y" "z"}} {"a[b][c]" "d"} {"a" {"b" {"c" "d"}}} {"a" "b", "c" "d"} {"a" "b", "c" "d"})) (testing "nested parameter lists" (are [p r] (= (handler {:params p}) r) {"foo[]" "bar"} {"foo" ["bar"]} {"foo[]" ["bar" "baz"]} {"foo" ["bar" "baz"]}) (let [params (handler {:params {"a[x][]" ["b"], "a[x][][y]" "c"}})] (is (= (keys params) ["a"])) (is (= (keys (params "a")) ["x"])) (is (= (set (get-in params ["a" "x"])) #{"b" {"y" "c"}}))) (let [params (handler {:params {"a[][x]" "c", "a[][y]" "d"}})] (is (= (keys params) ["a"])) (is (= (set (params "a")) #{{"x" "c"} {"y" "d"}})))) (testing "duplicate parameters" (are [p r] (= (handler {:params p}) r) {"a" ["b" "c"]} {"a" ["b" "c"]} {"a[b]" ["c" "d"]} {"a" {"b" ["c" "d"]}})) (testing "parameters with newlines" (are [p r] (= (handler {:params p}) r) {"foo\nbar" "baz"} {"foo\nbar" "baz"})) (testing "empty string" (is (= {"foo" {"bar" "baz"}} (handler {:params (sorted-map "foo" "" "foo[bar]" "baz")})))) (testing "double nested empty string" (is (= {"foo" {"bar" {"baz" "bam"}}} (handler {:params (sorted-map "foo[bar]" "" "foo[bar][baz]" "bam")})))) (testing "parameters are already nested" (is (= {"foo" [["bar" "baz"] ["asdf" "zxcv"]]} (handler {:params {"foo" [["bar" "baz"] ["asdf" "zxcv"]]}})))) (testing "pre-nested vector of maps" (is (= {"foo" [{"bar" "baz"} {"asdf" "zxcv"}]} (handler {:params {"foo" [{"bar" "baz"} {"asdf" "zxcv"}]}})))) (testing "pre-nested map" (is (= {"foo" [{"bar" "baz" "asdf" "zxcv"}]} (handler {:params {"foo" [{"bar" "baz" "asdf" "zxcv"}]}})))) (testing "double-nested map" (is (= {"foo" {"key" {"bar" "baz" "asdf" "zxcv"}}} (handler {:params {"foo" {"key" {"bar" "baz" "asdf" "zxcv"}}}})))))) (deftest nested-params-test-with-options (let [handler (wrap-nested-params :params {:key-parser #(string/split % #"\.")})] (testing ":key-parser option" (are [p r] (= (handler {:params p}) r) {"foo" "bar"} {"foo" "bar"} {"x.y" "z"} {"x" {"y" "z"}} {"a.b.c" "d"} {"a" {"b" {"c" "d"}}} {"a" "b", "c" "d"} {"a" "b", "c" "d"})))) (deftest wrap-nested-params-cps-test (let [handler (wrap-nested-params (fn [req respond _] (respond (:params req)))) request {:params {"x[y]" "z"}} response (promise) exception (promise)] (handler request response exception) (is (= {"x" {"y" "z"}} @response)) (is (not (realized? exception))))) (deftest nested-params-request-test (is (fn? nested-params-request))) ring-1.6.2/ring-core/test/ring/middleware/test/not_modified.clj000066400000000000000000000110171313243611400245060ustar00rootroot00000000000000(ns ring.middleware.test.not-modified (:require [clojure.test :refer :all] [ring.middleware.not-modified :refer :all])) (defn- handler-etag [etag] (constantly {:status 200 :headers {"etag" etag} :body ""})) (defn- handler-modified [modified] (constantly {:status 200 :headers {"last-modified" modified} :body ""})) (defn- etag-request [etag] {:request-method :get, :headers {"if-none-match" etag}}) (defn- modified-request [modified-date] {:request-method :get, :headers {"if-modified-since" modified-date}}) (deftest test-wrap-not-modified (with-redefs [ring.middleware.not-modified/not-modified-response #(vector %1 %2)] (let [req (modified-request "Sun, 23 Sep 2012 11:00:00 GMT") handler (handler-modified "Jan, 23 Sep 2012 11:00:00 GMT")] (is (= [(handler req) req] ; Not modified since is called with expected args ((wrap-not-modified handler) req)))))) (deftest wrap-not-modified-cps-test (testing "etag match" (let [etag "known-etag" handler (wrap-not-modified (fn [req respond _] (respond {:status 200, :headers {"etag" etag}, :body ""}))) request {:request-method :get, :headers {"if-none-match" etag}} response (promise) exception (promise)] (handler request response exception) (is (= 304 (:status @response))) (is (not (realized? exception)))))) (deftest test-not-modified-response (testing "etag match" (let [known-etag "known-etag" request {:request-method :get, :headers {"if-none-match" known-etag}} handler-resp #(hash-map :status 200 :headers {"etag" %} :body "")] (is (= 304 (:status (not-modified-response (handler-resp known-etag) request)))) (is (= 200 (:status (not-modified-response (handler-resp "unknown-etag") request)))))) (testing "not modified" (let [req #(hash-map :request-method :get, :headers {"if-modified-since" %}) last-modified "Sun, 23 Sep 2012 11:00:00 GMT" h-resp {:status 200 :headers {"Last-Modified" last-modified} :body ""}] (is (= 304 (:status (not-modified-response h-resp (req last-modified))))) (is (= 304 (:status (not-modified-response h-resp (req "Sun, 23 Sep 2012 11:52:50 GMT"))))) (is (= 200 (:status (not-modified-response h-resp (req "Sun, 23 Sep 2012 10:00:50 GMT"))))))) (testing "not modified body and content-length" (let [req #(hash-map :request-method :get :headers {"if-modified-since" %}) last-modified "Sun, 23 Sep 2012 11:00:00 GMT" h-resp {:status 200 :headers {"Last-Modified" last-modified} :body ""} resp (not-modified-response h-resp (req last-modified))] (is (nil? (:body resp))) (is (= (get-in resp [:headers "Content-Length"]) "0")))) (testing "no modification info" (let [response {:status 200 :headers {} :body ""}] (is (= 200 (:status (not-modified-response response (etag-request "\"12345\""))))) (is (= 200 (:status (not-modified-response response (modified-request "Sun, 23 Sep 2012 10:00:00 GMT"))))))) (testing "header case insensitivity" (let [h-resp {:status 200 :headers {"LasT-ModiFied" "Sun, 23 Sep 2012 11:00:00 GMT" "EtAg" "\"123456abcdef\""}}] (is (= 304 (:status (not-modified-response h-resp {:request-method :get :headers {"if-modified-since" "Sun, 23 Sep 2012 11:00:00 GMT"}})))) (is (= 304 (:status (not-modified-response h-resp {:request-method :get :headers {"if-none-match" "\"123456abcdef\""}})))))) (testing "doesn't affect POST, PUT, PATCH or DELETE" (let [date "Sat, 22 Sep 2012 11:00:00 GMT" req {:headers {"if-modified-since" date}} resp {:status 200 :headers {"Last-Modified" date} :body ""}] (are [m s] (= s (:status (not-modified-response resp (assoc req :request-method m)))) :head 304 :get 304 :post 200 :put 200 :patch 200 :delete 200))) (testing "only affects 200 OK responses" (let [date "Sat, 22 Sep 2012 11:00:00 GMT" req {:request-method :get, :headers {"if-modified-since" date}} resp {:headers {"Last-Modified" date} :body ""}] (are [s1 s2] (= s2 (:status (not-modified-response (assoc resp :status s1) req))) 200 304 302 302 404 404 500 500)))) ring-1.6.2/ring-core/test/ring/middleware/test/params.clj000066400000000000000000000050131313243611400233300ustar00rootroot00000000000000(ns ring.middleware.test.params (:require [clojure.test :refer :all] [ring.middleware.params :refer :all] [ring.util.io :refer [string-input-stream]])) (def wrapped-echo (wrap-params identity)) (deftest wrap-params-query-params-only (let [req {:query-string "foo=bar&biz=bat%25"} resp (wrapped-echo req)] (is (= {"foo" "bar" "biz" "bat%"} (:query-params resp))) (is (empty? (:form-params resp))) (is (= {"foo" "bar" "biz" "bat%"} (:params resp))))) (deftest wrap-params-query-and-form-params (let [req {:query-string "foo=bar" :headers {"content-type" "application/x-www-form-urlencoded"} :body (string-input-stream "biz=bat%25")} resp (wrapped-echo req)] (is (= {"foo" "bar"} (:query-params resp))) (is (= {"biz" "bat%"} (:form-params resp))) (is (= {"foo" "bar" "biz" "bat%"} (:params resp))))) (deftest wrap-params-not-form-encoded (let [req {:headers {"content-type" "application/json"} :body (string-input-stream "{foo: \"bar\"}")} resp (wrapped-echo req)] (is (empty? (:form-params resp))) (is (empty? (:params resp))))) (deftest wrap-params-always-assocs-maps (let [req {:query-string "" :headers {"content-type" "application/x-www-form-urlencoded"} :body (string-input-stream "")} resp (wrapped-echo req)] (is (= {} (:query-params resp))) (is (= {} (:form-params resp))) (is (= {} (:params resp))))) (deftest wrap-params-encoding (let [req {:headers {"content-type" "application/x-www-form-urlencoded;charset=UTF-16"} :body (string-input-stream "hello=world" "UTF-16")} resp (wrapped-echo req)] (is (= (:params resp) {"hello" "world"})) (is (= (:form-params resp) {"hello" "world"})))) (deftest wrap-params-cps-test (let [handler (wrap-params (fn [req respond _] (respond req))) request {:query-string "foo=bar" :headers {"content-type" "application/x-www-form-urlencoded"} :body (string-input-stream "biz=bat%25")} response (promise) exception (promise)] (handler request response exception) (is (= {"foo" "bar"} (:query-params @response))) (is (= {"biz" "bat%"} (:form-params @response))) (is (= {"foo" "bar" "biz" "bat%"} (:params @response))) (is (not (realized? exception))))) (deftest params-request-test (is (fn? params-request))) (deftest assoc-form-params-test (is (fn? assoc-form-params))) ring-1.6.2/ring-core/test/ring/middleware/test/resource.clj000066400000000000000000000067661313243611400237140ustar00rootroot00000000000000(ns ring.middleware.test.resource (:require [clojure.test :refer :all] [clojure.java.io :as io] [ring.middleware.resource :refer :all] [ring.util.io :refer [string-input-stream]]) (:import [java.net URL URLClassLoader])) (defn test-handler [request] {:status 200 :headers {} :body (string-input-stream "handler")}) (deftest resource-test (let [handler (wrap-resource test-handler "/ring/assets")] (are [request body] (= (slurp (:body (handler request))) body) {:request-method :get, :uri "/foo.html"} "foo" {:request-method :get, :uri "/index.html"} "index" {:request-method :get, :uri "/bars/foo.html"} "foo" {:request-method :get, :uri "/handler"} "handler" {:request-method :post, :uri "/foo.html"} "handler" {:request-method :get, :uri "/pre/foo.html" :path-info "/foo.html", :context "/pre"} "foo"))) (deftest resource-loader-test (let [root-loader (->> (Thread/currentThread) .getContextClassLoader (iterate (memfn getParent)) (take-while identity) last) jarfile (io/file "test/resource.jar") urls (into-array URL [(.toURL (.toURI jarfile))]) loader (URLClassLoader. urls root-loader) no-loader (wrap-resource test-handler "/ring/assets") with-loader (wrap-resource test-handler "/ring/assets" {:loader loader})] (are [request body] (= (slurp (:body (no-loader request))) body) {:request-method :get, :uri "/foo.html"} "foo") (are [request body] (= (slurp (:body (with-loader request))) body) {:request-method :get, :uri "/foo.html"} "foo-in-jar"))) (deftest wrap-resource-symlinks-test (testing "doesn't follow symlinks by default" (let [handler (wrap-resource test-handler "/ring/assets/bars") response (handler {:request-method :get, :uri "/backlink/foo.html"})] (is (= (slurp (:body response)) "handler")))) (testing "does follow symlinks when option is set" (let [handler (wrap-resource test-handler "/ring/assets/bars" {:allow-symlinks? true}) response (handler {:request-method :get, :uri "/backlink/foo.html"})] (is (= (slurp (:body response)) "foo"))))) (deftest wrap-resource-cps-test (let [handler (-> (fn [req respond _] (respond (test-handler req))) (wrap-resource "/ring/assets"))] (testing "resource response" (let [response (promise) exception (promise)] (handler {:request-method :get, :uri "/foo.html"} response exception) (is (= "foo" (-> @response :body slurp))) (is (not (realized? exception))))) (testing "non-resource response" (let [response (promise) exception (promise)] (handler {:request-method :get, :uri "/handler"} response exception) (is (= "handler" (-> @response :body slurp))) (is (not (realized? exception))))))) (deftest resource-request-test (is (fn? resource-request))) (deftest test-head-request (testing "middleware" (let [handler (wrap-resource test-handler "/ring/assets") response (handler {:request-method :head, :uri "/foo.html"})] (is (= (:status response) 200)) (is (nil? (:body response))))) (testing "request fn" (let [request {:request-method :head, :uri "/foo.html"} response (resource-request request "/ring/assets")] (is (= (:status response) 200)) (is (nil? (:body response)))))) ring-1.6.2/ring-core/test/ring/middleware/test/session.clj000066400000000000000000000212171313243611400235340ustar00rootroot00000000000000(ns ring.middleware.test.session (:require [clojure.test :refer :all] [ring.middleware.session :refer :all] [ring.middleware.session.store :refer :all] [ring.middleware.session.memory :refer [memory-store]])) (defn- make-store [reader writer deleter] (reify SessionStore (read-session [_ k] (reader k)) (write-session [_ k s] (writer k s)) (delete-session [_ k] (deleter k)))) (defn trace-fn [f] (let [trace (atom [])] (with-meta (fn [& args] (swap! trace conj args) (apply f args)) {:trace trace}))) (defn trace [f] (-> f meta :trace deref)) (defn get-cookies [response] (get-in response [:headers "Set-Cookie"])) (defn is-session-cookie? [c] (.contains c "ring-session=")) (defn get-session-cookie [response] (first (filter is-session-cookie? (get-cookies response)))) (deftest session-is-read (let [reader (trace-fn (constantly {:bar "foo"})) writer (trace-fn (constantly nil)) deleter (trace-fn (constantly nil)) store (make-store reader writer deleter) handler (trace-fn (constantly {})) handler* (wrap-session handler {:store store})] (handler* {:cookies {"ring-session" {:value "test"}}}) (is (= (trace reader) [["test"]])) (is (= (trace writer) [])) (is (= (trace deleter) [])) (is (= (-> handler trace first first :session) {:bar "foo"})))) (deftest session-is-written (let [reader (trace-fn (constantly {})) writer (trace-fn (constantly nil)) deleter (trace-fn (constantly nil)) store (make-store reader writer deleter) handler (constantly {:session {:foo "bar"}}) handler (wrap-session handler {:store store})] (handler {:cookies {}}) (is (= (trace reader) [[nil]])) (is (= (trace writer) [[nil {:foo "bar"}]])) (is (= (trace deleter) [])))) (deftest session-is-deleted (let [reader (trace-fn (constantly {})) writer (trace-fn (constantly nil)) deleter (trace-fn (constantly nil)) store (make-store reader writer deleter) handler (constantly {:session nil}) handler (wrap-session handler {:store store})] (handler {:cookies {"ring-session" {:value "test"}}}) (is (= (trace reader) [["test"]])) (is (= (trace writer) [])) (is (= (trace deleter) [["test"]])))) (deftest session-write-outputs-cookie (let [store (make-store (constantly {}) (constantly "foo:bar") (constantly nil)) handler (constantly {:session {:foo "bar"}}) handler (wrap-session handler {:store store}) response (handler {:cookies {}})] (is (get-session-cookie response)))) (deftest session-delete-outputs-cookie (let [store (make-store (constantly {:foo "bar"}) (constantly nil) (constantly "deleted")) handler (constantly {:session nil}) handler (wrap-session handler {:store store}) response (handler {:cookies {"ring-session" {:value "foo:bar"}}})] (is (.contains (get-session-cookie response) "ring-session=deleted")))) (deftest session-cookie-has-attributes (let [store (make-store (constantly {}) (constantly "foo:bar") (constantly nil)) handler (constantly {:session {:foo "bar"}}) handler (wrap-session handler {:store store :cookie-attrs {:max-age 5 :path "/foo"}}) response (handler {:cookies {}}) session-cookie (get-session-cookie response)] (is (and (.contains session-cookie "ring-session=foo%3Abar") (.contains session-cookie "Max-Age=5") (.contains session-cookie "Path=/foo") (.contains session-cookie "HttpOnly"))))) (deftest session-does-not-clobber-response-cookies (let [store (make-store (constantly {}) (constantly "foo:bar") (constantly nil)) handler (constantly {:session {:foo "bar"} :cookies {"cookie2" "value2"}}) handler (wrap-session handler {:store store :cookie-attrs {:max-age 5}}) response (handler {:cookies {}})] (is (= (first (remove is-session-cookie? (get-cookies response))) "cookie2=value2")))) (deftest session-root-can-be-set (let [store (make-store (constantly {}) (constantly "foo:bar") (constantly nil)) handler (constantly {:session {:foo "bar"}}) handler (wrap-session handler {:store store, :root "/foo"}) response (handler {:cookies {}})] (is (.contains (get-session-cookie response) "Path=/foo")))) (deftest session-attrs-can-be-set-per-request (let [store (make-store (constantly {}) (constantly "foo:bar") (constantly nil)) handler (constantly {:session {:foo "bar"} :session-cookie-attrs {:max-age 5}}) handler (wrap-session handler {:store store}) response (handler {:cookies {}})] (is (.contains (get-session-cookie response) "Max-Age=5")))) (deftest cookie-attrs-override-is-respected (let [store (make-store (constantly {}) (constantly {}) (constantly nil)) handler (constantly {:session {}}) handler (wrap-session handler {:store store :cookie-attrs {:http-only false}}) response (handler {:cookies {}})] (is (not (.contains (get-session-cookie response) "HttpOnly"))))) (deftest session-response-is-nil (let [handler (wrap-session (constantly nil))] (is (nil? (handler {}))))) (deftest session-made-up-key (let [store-ref (atom {}) store (make-store #(@store-ref %) #(do (swap! store-ref assoc %1 %2) %1) #(do (swap! store-ref dissoc %) nil)) handler (wrap-session (constantly {:session {:foo "bar"}}) {:store store})] (handler {:cookies {"ring-session" {:value "faked-key"}}}) (is (not (contains? @store-ref "faked-key"))))) (deftest session-request-test (is (fn? session-request))) (deftest session-response-test (is (fn? session-response))) (deftest session-cookie-attrs-change (let [a-resp (atom {:session {:foo "bar"}}) handler (wrap-session (fn [req] @a-resp)) response (handler {}) sess-key (->> (get-in response [:headers "Set-Cookie"]) (first) (re-find #"(?<==)[^;]+"))] (is (not (nil? sess-key))) (reset! a-resp {:session-cookie-attrs {:max-age 3600}}) (testing "Session cookie attrs with no active session" (is (= (handler {}) {}))) (testing "Session cookie attrs with active session" (let [response (handler {:foo "bar" :cookies {"ring-session" {:value sess-key}}})] (is (get-session-cookie response)))))) (deftest session-is-recreated-when-recreate-key-present-in-metadata (let [reader (trace-fn (constantly {})) writer (trace-fn (constantly nil)) deleter (trace-fn (constantly nil)) store (make-store reader writer deleter) handler (constantly {:session ^:recreate {:foo "bar"}}) handler (wrap-session handler {:store store})] (handler {:cookies {"ring-session" {:value "test"}}}) (is (= (trace reader) [["test"]])) (is (= (trace writer) [[nil {:foo "bar"}]])) (is (= (trace deleter) [["test"]])) (testing "session was not written with :recreate metadata intact" (let [[[_ written]] (trace writer)] (is (not (:recreate (meta written)))))))) (deftest wrap-sesssion-cps-test (testing "reading session" (let [memory (atom {"test" {:foo "bar"}}) store (memory-store memory) handler (-> (fn [req respond _] (respond (:session req))) (wrap-session {:store store})) request {:cookies {"ring-session" {:value "test"}}} response (promise) exception (promise)] (handler request response exception) (is (= {:foo "bar"} @response)) (is (= {"test" {:foo "bar"}} @memory)) (is (not (realized? exception))))) (testing "writing session" (let [memory (atom {"test" {}}) store (memory-store memory) handler (-> (fn [_ respond _] (respond {:session {:foo "bar"}, :body "foo"})) (wrap-session {:store store})) request {:cookies {"ring-session" {:value "test"}}} response (promise) exception (promise)] (handler request response exception) (is (= "foo" (:body @response))) (is (= {"test" {:foo "bar"}} @memory)) (is (not (realized? exception)))))) ring-1.6.2/ring-core/test/ring/util/000077500000000000000000000000001313243611400172355ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/util/test/000077500000000000000000000000001313243611400202145ustar00rootroot00000000000000ring-1.6.2/ring-core/test/ring/util/test/io.clj000066400000000000000000000013111313243611400213110ustar00rootroot00000000000000(ns ring.util.test.io (:require [clojure.test :refer :all] [clojure.java.io :as io] [ring.util.io :refer :all]) (:import [java.io IOException])) (deftest test-piped-input-stream (let [stream (piped-input-stream #(spit % "Hello World"))] (is (= (slurp stream) "Hello World")))) (deftest test-string-input-stream (let [stream (string-input-stream "Hello World")] (is (= (slurp stream) "Hello World")))) (deftest test-close! (testing "non-streams" (is (nil? (close! "foo")))) (testing "streams" (let [stream (piped-input-stream #(spit % "Hello World"))] (close! stream) (is (thrown? IOException (slurp stream))) (is (nil? (close! stream)))))) ring-1.6.2/ring-core/test/ring/util/test/mime_type.clj000066400000000000000000000011461313243611400227000ustar00rootroot00000000000000(ns ring.util.test.mime-type (:require [clojure.test :refer :all] [ring.util.mime-type :refer :all])) (deftest ext-mime-type-test (testing "default mime types" (are [f m] (= (ext-mime-type f) m) "foo.txt" "text/plain" "foo.html" "text/html" "foo.png" "image/png")) (testing "custom mime types" (is (= (ext-mime-type "foo.bar" {"bar" "application/bar"}) "application/bar")) (is (= (ext-mime-type "foo.txt" {"txt" "application/text"}) "application/text"))) (testing "case insensitivity" (is (= (ext-mime-type "FOO.TXT") "text/plain")))) ring-1.6.2/ring-core/test/ring/util/test/request.clj000066400000000000000000000061111313243611400223750ustar00rootroot00000000000000(ns ring.util.test.request (:require [clojure.test :refer :all] [ring.util.request :refer :all] [ring.util.io :refer [string-input-stream]]) (:import [java.io File])) (deftest test-request-url (is (= (request-url {:scheme :http :uri "/foo/bar" :headers {"host" "example.com"} :query-string "x=y"}) "http://example.com/foo/bar?x=y")) (is (= (request-url {:scheme :http :uri "/" :headers {"host" "localhost:8080"}}) "http://localhost:8080/")) (is (= (request-url {:scheme :https :uri "/index.html" :headers {"host" "www.example.com"}}) "https://www.example.com/index.html"))) (deftest test-content-type (testing "no content-type" (is (= (content-type {:headers {}}) nil))) (testing "content type with no charset" (is (= (content-type {:headers {"content-type" "text/plain"}}) "text/plain"))) (testing "content type with charset" (is (= (content-type {:headers {"content-type" "text/plain; charset=UTF-8"}}) "text/plain")))) (deftest test-content-length (testing "no content-length header" (is (= (content-length {:headers {}}) nil))) (testing "a content-length header" (is (= (content-length {:headers {"content-length" "1337"}}) 1337)))) (deftest test-character-encoding (testing "no content-type" (is (= (character-encoding {:headers {}}) nil))) (testing "content-type with no charset" (is (= (character-encoding {:headers {"content-type" "text/plain"}}) nil))) (testing "content-type with charset" (is (= (character-encoding {:headers {"content-type" "text/plain; charset=UTF-8"}}) "UTF-8")) (is (= (character-encoding {:headers {"content-type" "text/plain;charset=UTF-8"}}) "UTF-8")))) (deftest test-urlencoded-form? (testing "urlencoded form" (is (urlencoded-form? {:headers {"content-type" "application/x-www-form-urlencoded"}})) (is (urlencoded-form? {:headers {"content-type" "application/x-www-form-urlencoded; charset=UTF-8"}}))) (testing "other content type" (is (not (urlencoded-form? {:headers {"content-type" "application/json"}})))) (testing "no content type" (is (not (urlencoded-form? {:headers {}}))))) (deftest test-body-string (testing "nil body" (is (= (body-string {:body nil}) nil))) (testing "string body" (is (= (body-string {:body "foo"}) "foo"))) (testing "file body" (let [f (File/createTempFile "ring-test" "")] (spit f "bar") (is (= (body-string {:body f}) "bar")) (.delete f))) (testing "input-stream body" (is (= (body-string {:body (string-input-stream "baz")}) "baz")))) (deftest test-in-context? (is (in-context? {:uri "/foo/bar"} "/foo")) (is (not (in-context? {:uri "/foo/bar"} "/bar")))) (deftest test-set-context (is (= (set-context {:uri "/foo/bar"} "/foo") {:uri "/foo/bar" :context "/foo" :path-info "/bar"})) (is (thrown? AssertionError (set-context {:uri "/foo/bar"} "/bar")))) ring-1.6.2/ring-core/test/ring/util/test/response.clj000066400000000000000000000253471313243611400225570ustar00rootroot00000000000000(ns ring.util.test.response (:require [clojure.test :refer :all] [clojure.java.io :as io] [ring.util.response :refer :all]) (:import [java.io File InputStream] [org.apache.commons io.FileUtils])) (deftest test-redirect (is (= {:status 302 :headers {"Location" "http://google.com"} :body ""} (redirect "http://google.com"))) (are [x y] (= (->> x (redirect "/foo") :status) y) :moved-permanently 301 :found 302 :see-other 303 :temporary-redirect 307 :permanent-redirect 308 300 300)) (deftest test-redirect-after-post (is (= {:status 303 :headers {"Location" "http://example.com"} :body ""} (redirect-after-post "http://example.com")))) (deftest test-not-found (is (= {:status 404 :headers {} :body "Not found"} (not-found "Not found")))) (deftest test-created (testing "with location and without body" (is (= {:status 201 :headers {"Location" "foobar/location"} :body nil} (created "foobar/location")))) (testing "with body and with location" (is (= {:status 201 :headers {"Location" "foobar/location"} :body "foobar"} (created "foobar/location" "foobar"))))) (deftest test-response (is (= {:status 200 :headers {} :body "foobar"} (response "foobar")))) (deftest test-status (is (= {:status 200 :body ""} (status {:status nil :body ""} 200)))) (deftest test-content-type (is (= {:status 200 :headers {"Content-Type" "text/html" "Content-Length" "10"}} (content-type {:status 200 :headers {"Content-Length" "10"}} "text/html")))) (deftest test-charset (testing "add charset" (is (= (charset {:status 200 :headers {"Content-Type" "text/html"}} "UTF-8") {:status 200 :headers {"Content-Type" "text/html; charset=UTF-8"}}))) (testing "replace existing charset" (is (= (charset {:status 200 :headers {"Content-Type" "text/html; charset=UTF-16"}} "UTF-8") {:status 200 :headers {"Content-Type" "text/html; charset=UTF-8"}}))) (testing "default content-type" (is (= (charset {:status 200 :headers {}} "UTF-8") {:status 200 :headers {"Content-Type" "text/plain; charset=UTF-8"}}))) (testing "case insensitive" (is (= (charset {:status 200 :headers {"content-type" "text/html"}} "UTF-8") {:status 200 :headers {"content-type" "text/html; charset=UTF-8"}})))) (deftest test-get-charset (testing "simple charset" (is (= (get-charset {:headers {"Content-Type" "text/plain; charset=UTF-8"}}) "UTF-8"))) (testing "case insensitive" (is (= (get-charset {:headers {"content-type" "text/plain; charset=UTF-16"}}) "UTF-16"))) (testing "missing charset" (is (nil? (get-charset {:headers {"Content-Type" "text/plain"}})))) (testing "missing content-type" (is (nil? (get-charset {:headers {}}))))) (deftest test-header (is (= {:status 200 :headers {"X-Foo" "Bar"}} (header {:status 200 :headers {}} "X-Foo" "Bar")))) (deftest test-response? (is (response? {:status 200, :headers {}})) (is (response? {:status 200, :headers {}, :body "Foo"})) (is (not (response? {}))) (is (not (response? {:users []})))) (defmacro with-classloader "Temporarily replaces the current context classloader with one that includes everything in dir" [[dir] & forms] `(let [current-thread# (Thread/currentThread) original-loader# (.getContextClassLoader current-thread#) new-loader# (java.net.URLClassLoader. (into-array [(.toURL ~dir)]) original-loader#)] (try (.setContextClassLoader current-thread# new-loader#) ~@forms (finally (.setContextClassLoader current-thread# original-loader#))))) (defn- make-jar-url [jar-path res-path] (io/as-url (str "jar:file:" jar-path "!/" res-path))) (deftest test-url-response (testing "resources from a jar file" (let [base-path (.getPath (io/resource "ring/assets/test-resource.jar")) root-url (make-jar-url base-path "public/") empty-resource (make-jar-url base-path "public/empty-resource") non-empty-resource (make-jar-url base-path "public/hi-resource")] (is (nil? (url-response root-url))) (is (slurp (:body (url-response empty-resource))) "") (is (slurp (:body (url-response non-empty-resource))) "hi")))) (deftest test-resource-response (testing "response map" (let [resp (resource-response "/ring/assets/foo.html")] (is (= (resp :status) 200)) (is (= (into #{} (keys (resp :headers))) #{"Content-Length" "Last-Modified"})) (is (= (slurp (resp :body)) "foo")))) (testing "with root option" (let [resp (resource-response "/foo.html" {:root "/ring/assets"})] (is (= (slurp (resp :body)) "foo")))) (testing "with child class-loader" (let [resource (File/createTempFile "response_test" nil)] (FileUtils/writeStringToFile resource "just testing") (with-classloader [(.getParentFile resource)] (let [resp (resource-response (.getName resource))] (is (= (slurp (resp :body)) "just testing")))))) (testing "missing resource" (is (nil? (resource-response "/missing/resource.clj")))) (testing "response body type" (let [body (:body (resource-response "ring/util/response.clj"))] (is (instance? File body)) (is (.startsWith (slurp body) "(ns ring.util.response"))) (let [body (:body (resource-response "clojure/java/io.clj"))] (is (instance? InputStream body)) (is (.contains (slurp body) "clojure.java.io")))) (testing "resource is a directory" (is (nil? (resource-response "/ring/assets")))) (testing "resource is a directory in a jar file" (is (nil? (resource-response "/clojure/lang")))) (testing "resource is a directory in a jar file with a trailing slash" (is (nil? (resource-response "/clojure/lang/")))) (testing "resource is a file with spaces in path" (let [resp (resource-response "/ring/assets/hello world.txt")] (is (= (:body resp) (.getAbsoluteFile (File. "test/ring/assets/hello world.txt")))) (is (= (slurp (:body resp)) "Hello World\n")))) (testing "nil resource-data values" (defmethod resource-data :http [_] {}) (with-redefs [io/resource (constantly (java.net.URL. "http://foo"))] (is (= (response nil) (resource-response "whatever"))))) (testing "resource path cannot contain '..'" (is (nil? (resource-response "../util/response.clj" {:root "ring/assets"}))) (is (nil? (resource-response "../../util/response.clj" {:root "ring/assets/bars" :allow-symlinks? true})))) (testing "resource response doesn't follow symlinks by default" (is (nil? (resource-response "backlink/foo.html" {:root "ring/assets/bars"}))) (is (nil? (resource-response "backlink/bars.txt" {:root "ring/assets/bars"})))) (testing "resource response can optionally follows symlinks" (let [resp (resource-response "backlink/foo.html" {:root "ring/assets/bars" :allow-symlinks? true})] (is (= (resp :status) 200)) (is (= (into #{} (keys (resp :headers))) #{"Content-Length" "Last-Modified"})) (is (= (get-in resp [:headers "Content-Length"]) "3")) (is (= (slurp (resp :body)) "foo")))) (comment ;; This test requires the ability to have file names in the source ;; tree with non-ASCII characters in them encoded as UTF-8. That ;; may be platform-specific. Comment out for now. ;; If this fails on Mac OS X, try again with the command line: ;; LC_CTYPE="UTF-8" lein test (testing "resource is a file with UTF-8 characters in path" (let [resp (resource-response "/ring/assets/abcíe.txt")] (is (= (:body resp) (.getAbsoluteFile (File. "test/ring/assets/abcíe.txt")))) (is (.contains (slurp (:body resp)) "UTF-8")))))) (deftest test-file-response (testing "response map" (let [resp (file-response "foo.html" {:root "test/ring/assets"})] (is (= (resp :status) 200)) (is (= (into #{} (keys (resp :headers))) #{"Content-Length" "Last-Modified"})) (is (= (get-in resp [:headers "Content-Length"]) "3")) (is (= (slurp (resp :body)) "foo")))) (testing "file path cannot contain '..' " (is (nil? (file-response "../../../project.clj" {:root "test/ring/assets"}))) (is (nil? (file-response "../../../project.clj" {:root "test/ring/assets/bars" :allow-symlinks? true})))) (testing "file response optionally follows symlinks" (let [resp (file-response "backlink/foo.html" {:root "test/ring/assets/bars" :allow-symlinks? true})] (is (= (resp :status) 200)) (is (= (into #{} (keys (resp :headers))) #{"Content-Length" "Last-Modified"})) (is (= (get-in resp [:headers "Content-Length"]) "3")) (is (= (slurp (resp :body)) "foo"))) (is (nil? (file-response "backlink/foo.html" {:root "test/ring/assets/bars"}))) (is (nil? (file-response "backlink/bars.txt" {:root "test/ring/assets/bars"}))))) (deftest test-set-cookie (is (= {:status 200 :headers {} :cookies {"Foo" {:value "Bar"}}} (set-cookie {:status 200 :headers {}} "Foo" "Bar"))) (is (= {:status 200 :headers {} :cookies {"Foo" {:value "Bar" :http-only true}}} (set-cookie {:status 200 :headers {}} "Foo" "Bar" {:http-only true})))) (deftest test-find-header (is (= (find-header {:headers {"Content-Type" "text/plain"}} "Content-Type") ["Content-Type" "text/plain"])) (is (= (find-header {:headers {"content-type" "text/plain"}} "Content-Type") ["content-type" "text/plain"])) (is (= (find-header {:headers {"Content-typE" "text/plain"}} "content-type") ["Content-typE" "text/plain"])) (is (nil? (find-header {:headers {"Content-Type" "text/plain"}} "content-length")))) (deftest test-get-header (is (= (get-header {:headers {"Content-Type" "text/plain"}} "Content-Type") "text/plain")) (is (= (get-header {:headers {"content-type" "text/plain"}} "Content-Type") "text/plain")) (is (= (get-header {:headers {"Content-typE" "text/plain"}} "content-type") "text/plain")) (is (nil? (get-header {:headers {"Content-Type" "text/plain"}} "content-length")))) (deftest test-update-header (is (= (update-header {:headers {"Content-Type" "text/plain"}} "content-type" str "; charset=UTF-8") {:headers {"Content-Type" "text/plain; charset=UTF-8"}})) (is (= (update-header {:headers {}} "content-type" str "; charset=UTF-8") {:headers {"content-type" "; charset=UTF-8"}}))) ring-1.6.2/ring-core/test/ring/util/test/time.clj000066400000000000000000000010401313243611400216370ustar00rootroot00000000000000(ns ring.util.test.time (:require [clojure.test :refer :all] [ring.util.time :refer :all] [clj-time.core :refer [date-time]] [clj-time.coerce :refer [to-date]])) (deftest test-parse-date (are [x y] (= (parse-date x) (to-date y)) "Sun, 06 Nov 1994 08:49:37 GMT" (date-time 1994 11 6 8 49 37) "Sunday, 06-Nov-94 08:49:37 GMT" (date-time 1994 11 6 8 49 37) "Sun Nov 6 08:49:37 1994" (date-time 1994 11 6 8 49 37) "'Sun, 06 Nov 1994 08:49:37 GMT'" (date-time 1994 11 6 8 49 37))) ring-1.6.2/ring-devel/000077500000000000000000000000001313243611400145115ustar00rootroot00000000000000ring-1.6.2/ring-devel/checkouts/000077500000000000000000000000001313243611400165015ustar00rootroot00000000000000ring-1.6.2/ring-devel/checkouts/ring-core000077700000000000000000000000001313243611400225422../../ring-coreustar00rootroot00000000000000ring-1.6.2/ring-devel/project.clj000066400000000000000000000013061313243611400166510ustar00rootroot00000000000000(defproject ring/ring-devel "1.6.2" :description "Ring development and debugging libraries." :url "https://github.com/ring-clojure/ring" :scm {:dir ".."} :license {:name "The MIT License" :url "http://opensource.org/licenses/MIT"} :dependencies [[org.clojure/clojure "1.5.1"] [ring/ring-core "1.6.2"] [hiccup "1.0.5"] [clj-stacktrace "0.2.8"] [ns-tracker "0.3.1"]] :aliases {"test-all" ["with-profile" "default:+1.6:+1.7:+1.8" "test"]} :profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]} :1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]}}) ring-1.6.2/ring-devel/resources/000077500000000000000000000000001313243611400165235ustar00rootroot00000000000000ring-1.6.2/ring-devel/resources/ring/000077500000000000000000000000001313243611400174625ustar00rootroot00000000000000ring-1.6.2/ring-devel/resources/ring/css/000077500000000000000000000000001313243611400202525ustar00rootroot00000000000000ring-1.6.2/ring-devel/resources/ring/css/dump.css000066400000000000000000000023631313243611400217350ustar00rootroot00000000000000/* Copyright (c) 2008, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.6.0 */ html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}del,ins{text-decoration:none;} h3.info { font-size: 1.6em; margin-left: 1em; padding-top: .5em; padding-bottom: .5em; } table.request { font-size: 1.1em; width: 800px; margin-left: 1em; margin-right: 1em; background: lightgrey; } table.request tr { line-height: 1.4em; } table.request td.key { padding-left: .5em; text-aligh: left; width: 150px; } table.request td.val { text-align: left; } ring-1.6.2/ring-devel/resources/ring/css/stacktrace.css000066400000000000000000000041721313243611400231140ustar00rootroot00000000000000/* Copyright (c) 2008, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.6.0 */ html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}del,ins{text-decoration:none;} body { font-family: sans-serif; background: #a00; padding: 1em; } #exception { background: #f2f2f2; color: #333; padding: 1em; } h1 { color: #800; font-size: 32pt; text-align: center; margin-bottom: 0.3em; } .message { font-size: 16pt; text-align: center; margin-bottom: 1em; } #causes h2 { font-size: 22pt; text-align: center; margin-bottom: 0.3em; } #causes h2 .class { color: #800; } #causes .message { font-size: 14pt; } .trace { width: 90%; margin: auto; overflow: scroll; } .trace table { width: 100%; font-size: 12pt; background: #dadada; border: 0.8em solid #dadada; margin-bottom: 1.5em; } .trace table tr.clojure { color: #222; } .trace table tr.java { color: #6a6a6a; } .trace td { padding-top: 0.4em; padding-bottom: 0.4em; } .trace td.method { padding-left: 1em; padding-right: 0.2em; text-aligh: left; } .trace td.source { padding-left: 0.2em; text-align: right; } .trace .views { width: 100%; background: #bcbcbc; padding: 0.5em 0; } .views .label, .views ul, .views li { display: inline-block; } .trace .views .label { padding: 0 1em; } .trace .views li { padding: 0 2em; cursor: pointer; } ring-1.6.2/ring-devel/src/000077500000000000000000000000001313243611400153005ustar00rootroot00000000000000ring-1.6.2/ring-devel/src/ring/000077500000000000000000000000001313243611400162375ustar00rootroot00000000000000ring-1.6.2/ring-devel/src/ring/handler/000077500000000000000000000000001313243611400176545ustar00rootroot00000000000000ring-1.6.2/ring-devel/src/ring/handler/dump.clj000066400000000000000000000034351313243611400213200ustar00rootroot00000000000000(ns ring.handler.dump "A handler that displays the received request map. This is useful for debugging new adapters." (:require [clojure.set :as set] [clojure.pprint :as pprint] [clojure.java.io :as io] [hiccup.core :refer [html h]] [hiccup.page :refer [doctype]] [hiccup.def :refer [defhtml]] [ring.util.response :refer [content-type response]])) (def ^:no-doc ring-keys '(:server-port :server-name :remote-addr :uri :query-string :scheme :request-method :content-type :content-length :character-encoding :ssl-client-cert :headers :body)) (defn- style-resource [path] (html [:style {:type "text/css"} (slurp (io/resource path))])) (defn- req-pair [key req] (html [:tr [:td.key (h (str key))] [:td.val (h (pr-str (key req)))]])) (defhtml ^:no-doc template [req] (doctype :xhtml-transitional) [:html {:xmlns "http://www.w3.org/1999/xhtml"} [:head [:title "Ring: Request Dump"] (style-resource "ring/css/dump.css")] [:body [:div#content [:h3.info "Ring Request Values"] [:table.request [:tbody (for [key ring-keys] (req-pair key req))]] (if-let [user-keys (set/difference (set (keys req)) (set ring-keys))] (html [:br] [:table.request.user [:tbody [:tr (for [key (sort user-keys)] (req-pair key req))]]]))]]]) (defn handle-dump "Returns a HTML response that shows the information in the request map. Also prints the request map to STDOUT." ([request] (pprint/pprint request) (println) (-> (response (template request)) (content-type "text/html"))) ([request respond raise] (respond (handle-dump request)))) ring-1.6.2/ring-devel/src/ring/middleware/000077500000000000000000000000001313243611400203545ustar00rootroot00000000000000ring-1.6.2/ring-devel/src/ring/middleware/lint.clj000066400000000000000000000077641313243611400220320ustar00rootroot00000000000000(ns ring.middleware.lint "Middleware that checks Ring requests and responses for correctness." (:require [clojure.set :as set] [clojure.string :as str]) (:import [java.io File InputStream] [java.security.cert X509Certificate])) (defn- lint "Asserts that spec applied to val returns logical truth, otherwise raises an exception with a message produced by applying format to the message-pattern argument and a printing of an invalid val." [val spec message] (try (if-not (spec val) (throw (Exception. (format "Ring lint error: specified %s, but %s was not" message (pr-str val))))) (catch Exception e (if-not (re-find #"^Ring lint error: " (.getMessage e)) (throw (Exception. (format "Ring lint error: exception occured when checking that %s on %s: %s" message (pr-str val) (.getMessage e)))) (throw e))))) (defn- check-req "Validates the request, throwing an exception on violations of the spec." [req] (lint req map? "Ring request must a Clojure map") (lint (:server-port req) integer? ":server-port must be an Integer") (lint (:server-name req) string? ":server-name must be a String") (lint (:remote-addr req) string? ":remote-addr must be a String") (lint (:uri req) #(and (string? %) (.startsWith ^String % "/")) ":uri must be a String starting with \"/\"") (lint (:query-string req) #(or (nil? %) (string? %)) ":query-string must be nil or a String") (lint (:scheme req) #{:http :https} ":scheme must be one of :http or :https") (lint (:request-method req) #(and (keyword? %) (= (name %) (str/lower-case (name %)))) ":request-method must be a lowercase keyword") (lint (:content-type req) #(or (nil? %) (string? %)) ":content-type must be nil or a String") (lint (:content-length req) #(or (nil? %) (integer? %)) ":content-length must be nil or an Integer") (lint (:character-encoding req) #(or (nil? %) (string? %)) ":character-encoding must be nil or a String") (lint (:ssl-client-cert req) #(or (nil? %) (instance? X509Certificate %)) ":ssl-client-cert must be nil or an X509Certificate") (let [headers (:headers req)] (lint headers map? ":headers must be a Clojure map") (doseq [[hname hval] headers] (lint hname string? "header names must be Strings") (lint hname #(= % (.toLowerCase ^String %)) "header names must be in lower case") (lint hval string? "header values must be strings"))) (lint (:body req) #(or (nil? %) (instance? InputStream %)) ":body must be nil or an InputStream")) (defn- check-resp "Validates the response, throwing an exception on violations of the spec" [resp] (lint resp map? "Ring response must be a Clojure map") (lint (:status resp) #(and (integer? %) (>= % 100)) ":status must be an Integer greater than or equal to 100") (let [headers (:headers resp)] (lint headers map? ":headers must be a Clojure map") (doseq [[hname hval] headers] (lint hname string? "header names must Strings") (lint hval #(or (string? %) (every? string? %)) "header values must be Strings or collections of Strings"))) (lint (:body resp) #(or (nil? %) (string? %) (seq? %) (instance? File %) (instance? InputStream %)) ":body must a String, ISeq, File, or InputStream")) (defn wrap-lint "Wrap a handler to validate incoming requests and outgoing responses according to the current Ring specification. An exception is raised if either the request or response is invalid." [handler] (fn ([request] (check-req request) (let [response (handler request)] (check-resp response) response)) ([request respond raise] (check-req request) (handler request (fn [response] (check-resp response) (respond response)) raise)))) ring-1.6.2/ring-devel/src/ring/middleware/reload.clj000066400000000000000000000027361313243611400223240ustar00rootroot00000000000000(ns ring.middleware.reload "Middleware that reloads modified namespaces on each request. This middleware should be limited to use in development environments." (:require [ns-tracker.core :refer [ns-tracker]])) (defn- reloader [dirs retry?] (let [modified-namespaces (ns-tracker dirs) load-queue (java.util.concurrent.LinkedBlockingQueue.)] (fn [] (locking load-queue (doseq [ns-sym (modified-namespaces)] (.offer load-queue ns-sym)) (loop [] (when-let [ns-sym (.peek load-queue)] (if retry? (do (require ns-sym :reload) (.remove load-queue)) (do (.remove load-queue) (require ns-sym :reload))) (recur))))))) (defn wrap-reload "Reload namespaces of modified files before the request is passed to the supplied handler. Accepts the following options: :dirs - A list of directories that contain the source files. Defaults to [\"src\"]. :reload-compile-errors? - If true, keep attempting to reload namespaces that have compile errors. Defaults to true." ([handler] (wrap-reload handler {})) ([handler options] (let [reload! (reloader (:dirs options ["src"]) (:reload-compile-errors? options true))] (fn ([request] (reload!) (handler request)) ([request respond raise] (reload!) (handler request respond raise)))))) ring-1.6.2/ring-devel/src/ring/middleware/stacktrace.clj000066400000000000000000000075551313243611400232060ustar00rootroot00000000000000(ns ring.middleware.stacktrace "Middleware that catches exceptions thrown by the handler, and reports the error and stacktrace via a webpage and STDERR log. This middleware is for debugging purposes, and should be limited to development environments." (:require [clojure.java.io :as io] [hiccup.core :refer [html h]] [hiccup.page :refer [html5]] [clj-stacktrace.core :refer :all] [clj-stacktrace.repl :refer :all] [ring.util.response :refer [content-type response status]])) (defn wrap-stacktrace-log "Wrap a handler such that exceptions are logged to *err* and then rethrown. Accepts the following options: :color? - if true, apply ANSI colors to stacktrace (default false)" ([handler] (wrap-stacktrace-log handler {})) ([handler options] (let [color? (:color? options)] (fn ([request] (try (handler request) (catch Throwable ex (pst-on *err* color? ex) (throw ex)))) ([request respond raise] (try (handler request respond (fn [ex] (pst-on *err* color? ex) (raise ex))) (catch Throwable ex (pst-on *err* color? ex) (throw ex)))))))) (defn- style-resource [path] (html [:style {:type "text/css"} (slurp (io/resource path))])) (defn- elem-partial [elem] (if (:clojure elem) [:tr.clojure [:td.source (h (source-str elem))] [:td.method (h (clojure-method-str elem))]] [:tr.java [:td.source (h (source-str elem))] [:td.method (h (java-method-str elem))]])) (defn- html-exception [ex] (let [[ex & causes] (iterate :cause (parse-exception ex))] (html5 [:head [:title "Ring: Stacktrace"] (style-resource "ring/css/stacktrace.css")] [:body [:div#exception [:h1 (h (.getName ^Class (:class ex)))] [:div.message (h (:message ex))] [:div.trace [:table [:tbody (map elem-partial (:trace-elems ex))]]] (for [cause causes :while cause] [:div#causes [:h2 "Caused by " [:span.class (h (.getName ^Class (:class cause)))]] [:div.message (h (:message cause))] [:div.trace [:table [:tbody (map elem-partial (:trace-elems cause))]]]])]]))) (defn- js-ex-response [e] (-> (response (with-out-str (pst e))) (status 500) (content-type "text/javascript"))) (defn- html-ex-response [ex] (-> (response (html-exception ex)) (status 500) (content-type "text/html"))) (defn- ex-response "Returns a response showing debugging information about the exception. Currently supports delegation to either js or html exception views." [req ex] (let [accept (get-in req [:headers "accept"])] (if (and accept (re-find #"^text/javascript" accept)) (js-ex-response ex) (html-ex-response ex)))) (defn wrap-stacktrace-web "Wrap a handler such that exceptions are caught and a response containing a HTML representation of the exception and stacktrace is returned." [handler] (fn ([request] (try (handler request) (catch Throwable ex (ex-response request ex)))) ([request respond raise] (try (handler request respond (fn [ex] (respond (ex-response request ex)))) (catch Throwable ex (respond (ex-response request ex))))))) (defn wrap-stacktrace "Wrap a handler such that exceptions are caught, a corresponding stacktrace is logged to *err*, and a HTML representation of the stacktrace is returned as a response. Accepts the following option: :color? - if true, apply ANSI colors to terminal stacktrace (default false)" {:arglists '([handler] [handler options])} ([handler] (wrap-stacktrace handler {})) ([handler options] (-> handler (wrap-stacktrace-log options) (wrap-stacktrace-web)))) ring-1.6.2/ring-devel/test/000077500000000000000000000000001313243611400154705ustar00rootroot00000000000000ring-1.6.2/ring-devel/test/ring/000077500000000000000000000000001313243611400164275ustar00rootroot00000000000000ring-1.6.2/ring-devel/test/ring/handler/000077500000000000000000000000001313243611400200445ustar00rootroot00000000000000ring-1.6.2/ring-devel/test/ring/handler/test/000077500000000000000000000000001313243611400210235ustar00rootroot00000000000000ring-1.6.2/ring-devel/test/ring/handler/test/dump.clj000066400000000000000000000015261313243611400224660ustar00rootroot00000000000000(ns ring.handler.test.dump (:require [clojure.test :refer :all] [ring.handler.dump :refer :all] [ring.util.io :refer [string-input-stream]])) (def post-req {:uri "/foo/bar" :request-method :post :body (string-input-stream "post body")}) (def get-req {:uri "/foo/bar" :request-method :get}) (deftest test-handle-dump (binding [*out* (java.io.StringWriter.)] (let [{:keys [status]} (handle-dump post-req)] (is (= 200 status))) (let [{:keys [status]} (handle-dump get-req)] (is (= 200 status))))) (deftest handle-dump-cps-test (binding [*out* (java.io.StringWriter.)] (let [response (promise) exception (promise)] (handle-dump post-req response exception) (is (= 200 (:status @response))) (is (not (realized? exception)))))) ring-1.6.2/ring-devel/test/ring/middleware/000077500000000000000000000000001313243611400205445ustar00rootroot00000000000000ring-1.6.2/ring-devel/test/ring/middleware/test/000077500000000000000000000000001313243611400215235ustar00rootroot00000000000000ring-1.6.2/ring-devel/test/ring/middleware/test/lint.clj000066400000000000000000000102551313243611400231660ustar00rootroot00000000000000(ns ring.middleware.test.lint (:require [clojure.test :refer :all] [ring.middleware.lint :refer :all] [ring.util.io :refer [string-input-stream]]) (:import [java.io File InputStream])) (def valid-request {:server-port 80 :server-name "localhost" :remote-addr "192.0.2.235" :uri "/" :query-string nil :scheme :http :request-method :get :headers {} :content-type nil :content-length nil :character-encoding nil :body nil}) (def valid-response {:status 200 :headers {} :body "valid"}) (def valid-response-app (wrap-lint (fn [req] valid-response))) (defn constant-app [constant-response] (wrap-lint (fn [req] constant-response))) (defn is-lint-error [f] (is (thrown-with-msg? Exception #"Ring lint error:.*" (f)))) (defmacro lints-req [key goods bads] (let [good-name (symbol (str "request-" key "-valid-values")) bad-name (symbol (str "request-" key "-invalid-values"))] `(do (deftest ~good-name (doseq [good# ~goods] (is (= valid-response (valid-response-app (assoc valid-request ~key good#))) (format "%s is a valid value for request key %s" (pr-str good#) ~key)))) (deftest ~bad-name (doseq [bad# ~bads] (is-lint-error (fn [] (valid-response-app (assoc valid-request ~key bad#))))))))) (defmacro lints-resp [key goods bads] (let [good-name (symbol (str "response-" key "-valid-values")) bad-name (symbol (str "response-" key "-invalid-values"))] `(do (deftest ~good-name (doseq [good# ~goods] (let [response# (assoc valid-response ~key good#) app# (constant-app response#)] (is (= response# (app# valid-request)) (format "%s is a valid value for response key %s" (pr-str good#) ~key))))) (deftest ~bad-name (doseq [bad# ~bads] (let [response# (assoc valid-response ~key bad#) app# (constant-app response#)] (is-lint-error (fn [] (app# valid-request))))))))) (lints-req :server-port [80 8080] [nil "80"]) (lints-req :server-name ["localhost" "www.amazon.com" "192.0.2.235"] [nil 123]) (lints-req :remote-addr ["192.0.2.235" "0:0:0:0:0:0:0:1%0"] [nil 123]) (lints-req :uri ["/" "/foo" "/foo/bar"] [nil ""]) (lints-req :query-string [nil "" "foo=bar" "foo=bar&biz=bat"] [:foo]) (lints-req :scheme [:http :https] [nil "http"]) (lints-req :request-method [:get :head :options :put :patch :post :delete] [nil :FOOBAR "get" 'get]) (lints-req :content-type [nil "text/html"] [:text/html]) (lints-req :content-length [nil 123] ["123"]) (lints-req :character-encoding [nil "UTF-8"] [:utf-8]) (lints-req :ssl-client-cert [nil (proxy [java.security.cert.X509Certificate] [] (toString [] "X509Certificate"))] ["01234567890ABCDEF"]) (lints-req :headers [{"foo" "bar"} {"bat" "Biz"} {"whiz-bang" "high-low"}] [nil {:foo "bar"} {"bar" :foo} {"Bar" "foo"}]) (lints-req :body [nil (string-input-stream "thebody")] ["thebody" :thebody]) (lints-resp :status [100 301 500] [nil "100" 99]) (lints-resp :headers [{} {"foo" "bar"} {"Biz" "Bat"} {"vert" ["high" "low"]} {"horz" #{"left right"}}] [nil {:foo "bar"} {"foo" :bar} {"dir" 123}]) (lints-resp :body [nil "thebody" (list "foo" "bar") (string-input-stream "thebody") (File. "test/ring/assets/foo.html")] [123 :thebody]) (deftest wrap-lint-cps-test (testing "valid request and response" (let [handler (wrap-lint (fn [_ respond _] (respond valid-response))) response (promise) exception (promise)] (handler valid-request response exception) (is (= valid-response @response)) (is (not (realized? exception))))) (testing "invalid request" (let [handler (wrap-lint (fn [_ respond _] (respond valid-response)))] (is-lint-error #(handler {} (fn [_]) (fn [_]))))) (testing "invalid response" (let [handler (wrap-lint (fn [_ respond _] (respond {})))] (is-lint-error #(handler valid-request (fn [_]) (fn [_])))))) ring-1.6.2/ring-devel/test/ring/middleware/test/reload.clj000066400000000000000000000011241313243611400234610ustar00rootroot00000000000000(ns ring.middleware.test.reload (:require [clojure.test :refer :all] [ring.middleware.reload :refer :all])) (deftest wrap-reload-smoke-test (let [handler (wrap-reload identity) request {:http-method :get, :uri "/"}] (is (= (handler request) request)))) (deftest wrap-reload-cps-test (let [handler (wrap-reload (fn [req respond _] (respond req))) request {:http-method :get, :uri "/"} response (promise) exception (promise)] (handler request response exception) (is (= request @response)) (is (not (realized? exception))))) ring-1.6.2/ring-devel/test/ring/middleware/test/stacktrace.clj000066400000000000000000000037121313243611400243440ustar00rootroot00000000000000(ns ring.middleware.test.stacktrace (:require [clojure.test :refer :all] [ring.middleware.stacktrace :refer :all])) (def exception-app (wrap-stacktrace (fn [_] (throw (Exception. "fail"))))) (def assert-app (wrap-stacktrace (fn [_] (assert (= 1 2))))) (def html-req {}) (def js-req {:headers {"accept" "text/javascript"}}) (deftest wrap-stacktrace-smoke (binding [*err* (java.io.StringWriter.)] (let [{:keys [status headers] :as response} (exception-app html-req)] (is (= 500 status)) (is (= {"Content-Type" "text/html"} headers))) (let [{:keys [status headers]} (exception-app js-req)] (is (= 500 status)) (is (= {"Content-Type" "text/javascript"} headers))) (let [{:keys [status headers] :as response} (assert-app html-req)] (is (= 500 status)) (is (= {"Content-Type" "text/html"} headers))) (let [{:keys [status headers]} (assert-app js-req)] (is (= 500 status)) (is (= {"Content-Type" "text/javascript"} headers))))) (deftest wrap-stacktrace-cps-test (testing "no exception" (let [handler (wrap-stacktrace (fn [_ respond _] (respond :ok))) response (promise) exception (promise)] (handler {} response exception) (is (= :ok @response)) (is (not (realized? exception))))) (testing "thrown exception" (let [handler (wrap-stacktrace (fn [_ _ _] (throw (Exception. "fail")))) response (promise) exception (promise)] (binding [*err* (java.io.StringWriter.)] (handler {} response exception)) (is (= 500 (:status @response))) (is (not (realized? exception))))) (testing "raised exception" (let [handler (wrap-stacktrace (fn [_ _ raise] (raise (Exception. "fail")))) response (promise) exception (promise)] (binding [*err* (java.io.StringWriter.)] (handler {} response exception)) (is (= 500 (:status @response))) (is (not (realized? exception)))))) ring-1.6.2/ring-jetty-adapter/000077500000000000000000000000001313243611400161675ustar00rootroot00000000000000ring-1.6.2/ring-jetty-adapter/checkouts/000077500000000000000000000000001313243611400201575ustar00rootroot00000000000000ring-1.6.2/ring-jetty-adapter/checkouts/ring-core000077700000000000000000000000001313243611400242202../../ring-coreustar00rootroot00000000000000ring-1.6.2/ring-jetty-adapter/checkouts/ring-servlet000077700000000000000000000000001313243611400255102../../ring-servletustar00rootroot00000000000000ring-1.6.2/ring-jetty-adapter/project.clj000066400000000000000000000014741313243611400203350ustar00rootroot00000000000000(defproject ring/ring-jetty-adapter "1.6.2" :description "Ring Jetty adapter." :url "https://github.com/ring-clojure/ring" :scm {:dir ".."} :license {:name "The MIT License" :url "http://opensource.org/licenses/MIT"} :dependencies [[org.clojure/clojure "1.5.1"] [ring/ring-core "1.6.2"] [ring/ring-servlet "1.6.2"] [org.eclipse.jetty/jetty-server "9.2.21.v20170120"]] :aliases {"test-all" ["with-profile" "default:+1.6:+1.7:+1.8" "test"]} :profiles {:dev {:dependencies [[clj-http "2.2.0"]] :jvm-opts ["-Dorg.eclipse.jetty.server.HttpChannelState.DEFAULT_TIMEOUT=500"]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]} :1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]}}) ring-1.6.2/ring-jetty-adapter/src/000077500000000000000000000000001313243611400167565ustar00rootroot00000000000000ring-1.6.2/ring-jetty-adapter/src/ring/000077500000000000000000000000001313243611400177155ustar00rootroot00000000000000ring-1.6.2/ring-jetty-adapter/src/ring/adapter/000077500000000000000000000000001313243611400213355ustar00rootroot00000000000000ring-1.6.2/ring-jetty-adapter/src/ring/adapter/jetty.clj000066400000000000000000000164431313243611400231760ustar00rootroot00000000000000(ns ring.adapter.jetty "A Ring adapter that uses the Jetty 9 embedded web server. Adapters are used to convert Ring handlers into running web servers." (:require [ring.util.servlet :as servlet]) (:import [org.eclipse.jetty.server Request Server ServerConnector ConnectionFactory HttpConfiguration HttpConnectionFactory SslConnectionFactory SecureRequestCustomizer] [org.eclipse.jetty.server.handler AbstractHandler] [org.eclipse.jetty.util.thread ThreadPool QueuedThreadPool] [org.eclipse.jetty.util.ssl SslContextFactory] [javax.servlet AsyncContext] [javax.servlet.http HttpServletRequest HttpServletResponse])) (defn- ^AbstractHandler proxy-handler [handler] (proxy [AbstractHandler] [] (handle [_ ^Request base-request request response] (let [request-map (servlet/build-request-map request) response-map (handler request-map)] (servlet/update-servlet-response response response-map) (.setHandled base-request true))))) (defn- ^AbstractHandler async-proxy-handler [handler timeout] (proxy [AbstractHandler] [] (handle [_ ^Request base-request ^HttpServletRequest request ^HttpServletResponse response] (let [^AsyncContext context (.startAsync request)] (.setTimeout context timeout) (handler (servlet/build-request-map request) (fn [response-map] (servlet/update-servlet-response response context response-map)) (fn [^Throwable exception] (.sendError response 500 (.getMessage exception)) (.complete context))) (.setHandled base-request true))))) (defn- ^ServerConnector server-connector [^Server server & factories] (ServerConnector. server (into-array ConnectionFactory factories))) (defn- ^HttpConfiguration http-config [options] (doto (HttpConfiguration.) (.setSendDateHeader (:send-date-header? options true)) (.setOutputBufferSize (:output-buffer-size options 32768)) (.setRequestHeaderSize (:request-header-size options 8192)) (.setResponseHeaderSize (:response-header-size options 8192)) (.setSendServerVersion (:send-server-version? options true)))) (defn- ^ServerConnector http-connector [server options] (let [http-factory (HttpConnectionFactory. (http-config options))] (doto (server-connector server http-factory) (.setPort (options :port 80)) (.setHost (options :host)) (.setIdleTimeout (options :max-idle-time 200000))))) (defn- ^SslContextFactory ssl-context-factory [options] (let [context (SslContextFactory.)] (if (string? (options :keystore)) (.setKeyStorePath context (options :keystore)) (.setKeyStore context ^java.security.KeyStore (options :keystore))) (.setKeyStorePassword context (options :key-password)) (cond (string? (options :truststore)) (.setTrustStorePath context (options :truststore)) (instance? java.security.KeyStore (options :truststore)) (.setTrustStore context ^java.security.KeyStore (options :truststore))) (when (options :trust-password) (.setTrustStorePassword context (options :trust-password))) (case (options :client-auth) :need (.setNeedClientAuth context true) :want (.setWantClientAuth context true) nil) (if-let [exclude-ciphers (options :exclude-ciphers)] (.addExcludeCipherSuites context (into-array String exclude-ciphers))) (if-let [exclude-protocols (options :exclude-protocols)] (.addExcludeProtocols context (into-array String exclude-protocols))) context)) (defn- ^ServerConnector ssl-connector [server options] (let [ssl-port (options :ssl-port 443) http-factory (HttpConnectionFactory. (doto (http-config options) (.setSecureScheme "https") (.setSecurePort ssl-port) (.addCustomizer (SecureRequestCustomizer.)))) ssl-factory (SslConnectionFactory. (ssl-context-factory options) "http/1.1")] (doto (server-connector server ssl-factory http-factory) (.setPort ssl-port) (.setHost (options :host)) (.setIdleTimeout (options :max-idle-time 200000))))) (defn- ^ThreadPool create-threadpool [options] (let [pool (QueuedThreadPool. ^Integer (options :max-threads 50))] (.setMinThreads pool (options :min-threads 8)) (when (:daemon? options false) (.setDaemon pool true)) pool)) (defn- ^Server create-server [options] (let [server (Server. (create-threadpool options))] (when (:http? options true) (.addConnector server (http-connector server options))) (when (or (options :ssl?) (options :ssl-port)) (.addConnector server (ssl-connector server options))) server)) (defn ^Server run-jetty "Start a Jetty webserver to serve the given handler according to the supplied options: :configurator - a function called with the Jetty Server instance :async? - if true, treat the handler as asynchronous :async-timeout - async context timeout in ms (defaults to 0, no timeout) :port - the port to listen on (defaults to 80) :host - the hostname to listen on :join? - blocks the thread until server ends (defaults to true) :daemon? - use daemon threads (defaults to false) :http? - listen on :port for HTTP traffic (defaults to true) :ssl? - allow connections over HTTPS :ssl-port - the SSL port to listen on (defaults to 443, implies :ssl? is true) :exclude-ciphers - When :ssl? is true, exclude these cipher suites :exclude-protocols - When :ssl? is true, exclude these protocols :keystore - the keystore to use for SSL connections :key-password - the password to the keystore :truststore - a truststore to use for SSL connections :trust-password - the password to the truststore :max-threads - the maximum number of threads to use (default 50) :min-threads - the minimum number of threads to use (default 8) :max-idle-time - the maximum idle time in milliseconds for a connection (default 200000) :client-auth - SSL client certificate authenticate, may be set to :need,:want or :none (defaults to :none) :send-date-header? - add a date header to the response (default true) :output-buffer-size - the response body buffer size (default 32768) :request-header-size - the maximum size of a request header (default 8192) :response-header-size - the maximum size of a response header (default 8192) :send-server-version? - add Server header to HTTP response (default true)" [handler options] (let [server (create-server (dissoc options :configurator))] (if (:async? options) (.setHandler server (async-proxy-handler handler (:async-timeout options 0))) (.setHandler server (proxy-handler handler))) (when-let [configurator (:configurator options)] (configurator server)) (try (.start server) (when (:join? options true) (.join server)) server (catch Exception ex (.stop server) (throw ex))))) ring-1.6.2/ring-jetty-adapter/test/000077500000000000000000000000001313243611400171465ustar00rootroot00000000000000ring-1.6.2/ring-jetty-adapter/test/keystore.jks000066400000000000000000000023561313243611400215320ustar00rootroot00000000000000jetty500 +*w윐啸U6pmE6#9TǕ${Q ]5@Ef[$äR* TD$9}4OnA.7wĸ zffiNK8d:Y07>o> (seq (.getConnectors s)) (mapcat #(seq (.getConnectionFactories %))) (filter #(instance? SslConnectionFactory %)) (first) (.getSslContextFactory))) (defn- exclude-ciphers [server] (set (.getExcludeCipherSuites (get-ssl-context-factory server)))) (defn- exclude-protocols [server] (set (.getExcludeProtocols (get-ssl-context-factory server)))) (def test-port (find-free-local-port)) (def test-ssl-port (find-free-local-port)) (def test-url (str "http://localhost:" test-port)) (def test-ssl-url (str "https://localhost:" test-ssl-port)) (def nil-keystore (doto (KeyStore/getInstance (KeyStore/getDefaultType)) (.load nil))) (def test-ssl-options {:port test-port :ssl? true :ssl-port test-ssl-port :keystore nil-keystore :key-password "hunter2" :join? false}) (deftest test-run-jetty (testing "HTTP server" (with-server hello-world {:port test-port} (let [response (http/get test-url)] (is (= (:status response) 200)) (is (.startsWith (get-in response [:headers "content-type"]) "text/plain")) (is (= (:body response) "Hello World"))))) (testing "HTTPS server" (with-server hello-world {:port test-port :ssl-port test-ssl-port :keystore "test/keystore.jks" :key-password "password"} (let [response (http/get test-ssl-url {:insecure? true})] (is (= (:status response) 200)) (is (= (:body response) "Hello World"))))) (testing "HTTPS-only server" (with-server hello-world {:http? false :port test-port :ssl-port test-ssl-port :keystore "test/keystore.jks" :key-password "password"} (let [response (http/get test-ssl-url {:insecure? true})] (is (= (:status response) 200)) (is (= (:body response) "Hello World"))) (is (thrown-with-msg? ConnectException #"Connection refused" (http/get test-url))))) (testing "configurator set to run last" (let [max-threads 20 new-handler (proxy [AbstractHandler] [] (handle [_ ^Request base-request request response])) configurator (fn [server] (.setMaxThreads (.getThreadPool server) max-threads) (.setHandler server new-handler)) server (run-jetty hello-world {:join? false :port test-port :configurator configurator})] (is (= (.getMaxThreads (.getThreadPool server)) max-threads)) (is (identical? new-handler (.getHandler server))) (is (= 1 (count (.getHandlers server)))) (.stop server))) (testing "setting daemon threads" (testing "default (daemon off)" (let [server (run-jetty hello-world {:port test-port :join? false})] (is (not (.. server getThreadPool isDaemon))) (.stop server))) (testing "daemon on" (let [server (run-jetty hello-world {:port test-port :join? false :daemon? true})] (is (.. server getThreadPool isDaemon)) (.stop server))) (testing "daemon off" (let [server (run-jetty hello-world {:port test-port :join? false :daemon? false})] (is (not (.. server getThreadPool isDaemon))) (.stop server)))) (testing "setting max idle timeout" (let [server (run-jetty hello-world {:port test-port :ssl-port test-ssl-port :keystore "test/keystore.jks" :key-password "password" :join? false :max-idle-time 5000}) connectors (. server getConnectors)] (is (= 5000 (. (first connectors) getIdleTimeout))) (is (= 5000 (. (second connectors) getIdleTimeout))) (.stop server))) (testing "using the default max idle time" (let [server (run-jetty hello-world {:port test-port :ssl-port test-ssl-port :keystore "test/keystore.jks" :key-password "password" :join? false}) connectors (. server getConnectors)] (is (= 200000 (. (first connectors) getIdleTimeout))) (is (= 200000 (. (second connectors) getIdleTimeout))) (.stop server))) (testing "setting min-threads" (let [server (run-jetty hello-world {:port test-port :min-threads 3 :join? false}) thread-pool (. server getThreadPool)] (is (= 3 (. thread-pool getMinThreads))) (.stop server))) (testing "default min-threads" (let [server (run-jetty hello-world {:port test-port :join? false}) thread-pool (. server getThreadPool)] (is (= 8 (. thread-pool getMinThreads))) (.stop server))) (testing "default character encoding" (with-server (content-type-handler "text/plain") {:port test-port} (let [response (http/get test-url)] (is (.contains (get-in response [:headers "content-type"]) "text/plain"))))) (testing "custom content-type" (with-server (content-type-handler "text/plain;charset=UTF-16;version=1") {:port test-port} (let [response (http/get test-url)] (is (= (get-in response [:headers "content-type"]) "text/plain;charset=UTF-16;version=1"))))) (testing "request translation" (with-server echo-handler {:port test-port} (let [response (http/post (str test-url "/foo/bar/baz?surname=jones&age=123") {:body "hello"})] (is (= (:status response) 200)) (is (= (:body response) "hello")) (let [request-map (read-string (get-in response [:headers "request-map"]))] (is (= (:query-string request-map) "surname=jones&age=123")) (is (= (:uri request-map) "/foo/bar/baz")) (is (= (:content-length request-map) 5)) (is (= (:character-encoding request-map) "UTF-8")) (is (= (:request-method request-map) :post)) (is (= (:content-type request-map) "text/plain; charset=UTF-8")) (is (= (:remote-addr request-map) "127.0.0.1")) (is (= (:scheme request-map) :http)) (is (= (:server-name request-map) "localhost")) (is (= (:server-port request-map) test-port)) (is (= (:ssl-client-cert request-map) nil)))))) (testing "sending 'Server' header in HTTP response'" (testing ":send-server-version? set to default value (true)" (with-server hello-world {:port test-port} (let [response (http/get test-url)] (is (contains? (:headers response) "Server"))))) (testing ":send-server-version? set to true" (with-server hello-world {:port test-port :send-server-version? true} (let [response (http/get test-url)] (is (contains? (:headers response) "Server"))))) (testing ":send-server-version? set to false" (with-server hello-world {:port test-port :send-server-version? false} (let [response (http/get test-url)] (is (not (contains? (:headers response) "Server"))))))) (testing "excluding cipher suites" (let [cipher "SSL_RSA_WITH_NULL_MD5" options (assoc test-ssl-options :exclude-ciphers [cipher]) server (run-jetty echo-handler options)] (try (is (contains? (exclude-ciphers server) cipher)) (finally (.stop server))))) (testing "excluding cipher protocols" (let [protocol "SSLv2Hello" options (assoc test-ssl-options :exclude-protocols [protocol]) server (run-jetty echo-handler options)] (try (is (contains? (exclude-protocols server) protocol)) (finally (.stop server))))) ;; Unable to get test working with Jetty 9 (comment (testing "resource cleanup on exception" (with-server hello-world {:port test-port} (let [thread-count (count (all-threads))] (is (thrown? Exception (run-jetty hello-world {:port test-port}))) (loop [i 0] (when (and (< i 400) (not= thread-count (count (all-threads)))) (Thread/sleep 250) (recur (inc i)))) (is (= thread-count (count (all-threads))))))))) (def thread-exceptions (atom [])) (defn- hello-world-cps [request respond raise] (respond {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"})) (defn- hello-world-cps-future [request respond raise] (future (try (respond {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"}) (catch Exception ex (swap! thread-exceptions conj ex))))) (defn- hello-world-streaming [request respond raise] (future (respond {:status 200 :headers {"Content-Type" "text/event-stream"} :body (reify p/StreamableResponseBody (write-body-to-stream [_ _ output] (future (with-open [w (io/writer output)] (Thread/sleep 100) (.write w "data: hello\n\n") (.flush w) (Thread/sleep 100) (.write w "data: world\n\n") (.flush w)))))}))) (defn- hello-world-streaming-long [request respond raise] (respond {:status 200 :headers {"Content-Type" "text/event-stream"} :body (reify p/StreamableResponseBody (write-body-to-stream [_ _ output] (future (with-open [w (io/writer output)] (dotimes [i 10] (Thread/sleep 100) (.write w (str "data: " i "\n\n")) (.flush w))))))})) (defn- error-cps [request respond raise] (raise (ex-info "test" {:foo "bar"}))) (defn- sometimes-error-cps [request respond raise] (if (= (:uri request) "/error") (error-cps request respond raise) (hello-world-cps request respond raise))) (deftest run-jetty-cps-test (testing "async response in future" (reset! thread-exceptions []) (with-server hello-world-cps-future {:port test-port, :async? true} (let [response (http/get test-url)] (Thread/sleep 100) (is (empty? @thread-exceptions)) (is (= (:status response) 200)) (is (.startsWith (get-in response [:headers "content-type"]) "text/plain")) (is (= (:body response) "Hello World"))))) (testing "async response" (with-server hello-world-cps {:port test-port, :async? true} (let [response (http/get test-url)] (is (= (:status response) 200)) (is (.startsWith (get-in response [:headers "content-type"]) "text/plain")) (is (= (:body response) "Hello World"))))) (testing "streaming response" (with-server hello-world-streaming {:port test-port, :async? true} (let [response (http/get test-url)] (is (= (:status response) 200)) (is (.startsWith (get-in response [:headers "content-type"]) "text/event-stream")) (is (= (:body response) "data: hello\n\ndata: world\n\n"))))) (testing "error response" (with-server error-cps {:port test-port, :async? true} (let [response (http/get test-url {:throw-exceptions false})] (is (= (:status response) 500))))) (testing "mixed error with normal responses" (with-server sometimes-error-cps {:port test-port, :async? true} (let [response (http/get (str test-url "/error") {:throw-exceptions false})] (is (= (:status response) 500))) (let [response (http/get test-url {:throw-exceptions false})] (is (= (:status response) 200))) (let [response (http/get (str test-url "/error") {:throw-exceptions false})] (is (= (:status response) 500))) (let [response (http/get test-url {:throw-exceptions false})] (is (= (:status response) 200))))) (testing "async context default" (with-server hello-world-streaming-long {:port test-port, :async? true} (let [response (http/get test-url)] (is (= (:body response) (apply str (for [i (range 10)] (str "data: " i "\n\n"))))))))) ring-1.6.2/ring-servlet/000077500000000000000000000000001313243611400150765ustar00rootroot00000000000000ring-1.6.2/ring-servlet/checkouts/000077500000000000000000000000001313243611400170665ustar00rootroot00000000000000ring-1.6.2/ring-servlet/checkouts/ring-core000077700000000000000000000000001313243611400231272../../ring-coreustar00rootroot00000000000000ring-1.6.2/ring-servlet/project.clj000066400000000000000000000013211313243611400172330ustar00rootroot00000000000000(defproject ring/ring-servlet "1.6.2" :description "Ring servlet utilities." :url "https://github.com/ring-clojure/ring" :scm {:dir ".."} :license {:name "The MIT License" :url "http://opensource.org/licenses/MIT"} :dependencies [[org.clojure/clojure "1.5.1"] [ring/ring-core "1.6.2"]] :aliases {"test-all" ["with-profile" "default:+1.6:+1.7:+1.8" "test"]} :profiles {:provided {:dependencies [[javax.servlet/javax.servlet-api "3.1.0"]]} :dev {:dependencies [[javax.servlet/javax.servlet-api "3.1.0"]]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} :1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]} :1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]}}) ring-1.6.2/ring-servlet/src/000077500000000000000000000000001313243611400156655ustar00rootroot00000000000000ring-1.6.2/ring-servlet/src/ring/000077500000000000000000000000001313243611400166245ustar00rootroot00000000000000ring-1.6.2/ring-servlet/src/ring/util/000077500000000000000000000000001313243611400176015ustar00rootroot00000000000000ring-1.6.2/ring-servlet/src/ring/util/servlet.clj000066400000000000000000000137621313243611400217700ustar00rootroot00000000000000(ns ring.util.servlet "Compatibility functions for turning a ring handler into a Java servlet." (:require [clojure.java.io :as io] [clojure.string :as string] [ring.core.protocols :as protocols]) (:import [java.io File InputStream FileInputStream] [java.util Locale] [javax.servlet AsyncContext] [javax.servlet.http HttpServlet HttpServletRequest HttpServletResponse])) (defn- get-headers "Creates a name/value map of all the request headers." [^HttpServletRequest request] (reduce (fn [headers, ^String name] (assoc headers (.toLowerCase name Locale/ENGLISH) (->> (.getHeaders request name) (enumeration-seq) (string/join ",")))) {} (enumeration-seq (.getHeaderNames request)))) (defn- get-content-length "Returns the content length, or nil if there is no content." [^HttpServletRequest request] (let [length (.getContentLength request)] (if (>= length 0) length))) (defn- get-client-cert "Returns the SSL client certificate of the request, if one exists." [^HttpServletRequest request] (first (.getAttribute request "javax.servlet.request.X509Certificate"))) (defn build-request-map "Create the request map from the HttpServletRequest object." [^HttpServletRequest request] {:server-port (.getServerPort request) :server-name (.getServerName request) :remote-addr (.getRemoteAddr request) :uri (.getRequestURI request) :query-string (.getQueryString request) :scheme (keyword (.getScheme request)) :request-method (keyword (.toLowerCase (.getMethod request) Locale/ENGLISH)) :protocol (.getProtocol request) :headers (get-headers request) :content-type (.getContentType request) :content-length (get-content-length request) :character-encoding (.getCharacterEncoding request) :ssl-client-cert (get-client-cert request) :body (.getInputStream request)}) (defn merge-servlet-keys "Associate servlet-specific keys with the request map for use with legacy systems." [request-map ^HttpServlet servlet ^HttpServletRequest request ^HttpServletResponse response] (merge request-map {:servlet servlet :servlet-request request :servlet-response response :servlet-context (.getServletContext servlet) :servlet-context-path (.getContextPath request)})) (defn- set-headers "Update a HttpServletResponse with a map of headers." [^HttpServletResponse response, headers] (doseq [[key val-or-vals] headers] (if (string? val-or-vals) (.setHeader response key val-or-vals) (doseq [val val-or-vals] (.addHeader response key val)))) ; Some headers must be set through specific methods (when-let [content-type (get headers "Content-Type")] (.setContentType response content-type))) (defn- make-output-stream [^HttpServletResponse response ^AsyncContext context] (let [os (.getOutputStream response)] (if (nil? context) os (proxy [java.io.FilterOutputStream] [os] (close [] (.close os) (.complete context)))))) (defn update-servlet-response "Update the HttpServletResponse using a response map. Takes an optional AsyncContext." ([response response-map] (update-servlet-response response nil response-map)) ([^HttpServletResponse response context response-map] (let [{:keys [status headers body]} response-map] (when (nil? response) (throw (NullPointerException. "HttpServletResponse is nil"))) (when (nil? response-map) (throw (NullPointerException. "Response map is nil"))) (when status (.setStatus response status)) (set-headers response headers) (let [output-stream (make-output-stream response context)] (protocols/write-body-to-stream body response-map output-stream))))) (defn- make-blocking-service-method [handler] (fn [servlet request response] (-> request (build-request-map) (merge-servlet-keys servlet request response) (handler) (->> (update-servlet-response response))))) (defn- make-async-service-method [handler] (fn [servlet ^HttpServletRequest request ^HttpServletResponse response] (let [^AsyncContext context (.startAsync request)] (handler (-> request (build-request-map) (merge-servlet-keys servlet request response)) (fn [response-map] (update-servlet-response response context response-map)) (fn [^Throwable exception] (.sendError response 500 (.getMessage exception)) (.complete context)))))) (defn make-service-method "Turns a handler into a function that takes the same arguments and has the same return value as the service method in the HttpServlet class." ([handler] (make-service-method handler {})) ([handler options] (if (:async? options) (make-async-service-method handler) (make-blocking-service-method handler)))) (defn servlet "Create a servlet from a Ring handler." ([handler] (servlet handler {})) ([handler options] (let [service-method (make-service-method handler options)] (proxy [HttpServlet] [] (service [request response] (service-method this request response)))))) (defmacro defservice "Defines a service method with an optional prefix suitable for being used by genclass to compile a HttpServlet class. For example: (defservice my-handler) (defservice \"my-prefix-\" my-handler)" ([handler] `(defservice "-" ~handler)) ([prefix handler] (if (map? handler) `(defservice "-" ~prefix ~handler) `(defservice ~prefix ~handler {}))) ([prefix handler options] `(let [service-method# (make-service-method ~handler ~options)] (defn ~(symbol (str prefix "service")) [servlet# request# response#] (service-method# servlet# request# response#))))) ring-1.6.2/ring-servlet/test/000077500000000000000000000000001313243611400160555ustar00rootroot00000000000000ring-1.6.2/ring-servlet/test/ring/000077500000000000000000000000001313243611400170145ustar00rootroot00000000000000ring-1.6.2/ring-servlet/test/ring/util/000077500000000000000000000000001313243611400177715ustar00rootroot00000000000000ring-1.6.2/ring-servlet/test/ring/util/test/000077500000000000000000000000001313243611400207505ustar00rootroot00000000000000ring-1.6.2/ring-servlet/test/ring/util/test/servlet.clj000066400000000000000000000200201313243611400231200ustar00rootroot00000000000000(ns ring.util.test.servlet (:require [clojure.test :refer :all] [ring.util.servlet :refer :all]) (:import [java.util Locale])) (defmacro ^:private with-locale [locale & body] `(let [old-locale# (Locale/getDefault)] (try (Locale/setDefault ~locale) (do ~@body) (finally (Locale/setDefault old-locale#))))) (defn- enumeration [coll] (let [e (atom coll)] (proxy [java.util.Enumeration] [] (hasMoreElements [] (not (empty? @e))) (nextElement [] (let [f (first @e)] (swap! e rest) f))))) (defn- async-context [completed] (proxy [javax.servlet.AsyncContext] [] (complete [] (reset! completed true)))) (defn- servlet-request [request] (let [attributes {"javax.servlet.request.X509Certificate" [(request :ssl-client-cert)]}] (proxy [javax.servlet.http.HttpServletRequest] [] (getServerPort [] (request :server-port)) (getServerName [] (request :server-name)) (getRemoteAddr [] (request :remote-addr)) (getRequestURI [] (request :uri)) (getQueryString [] (request :query-string)) (getContextPath [] (request :servlet-context-path)) (getScheme [] (name (request :scheme))) (getMethod [] (-> request :request-method name .toUpperCase)) (getProtocol [] (request :protocol)) (getHeaderNames [] (enumeration (keys (request :headers)))) (getHeaders [name] (enumeration (get-in request [:headers name]))) (getContentType [] (request :content-type)) (getContentLength [] (or (request :content-length) -1)) (getCharacterEncoding [] (request :character-encoding)) (getAttribute [k] (attributes k)) (getInputStream [] (request :body)) (startAsync [] (async-context (request :completed)))))) (defn- servlet-response [response] (let [output-stream (java.io.ByteArrayOutputStream.)] (swap! response assoc :body output-stream) (proxy [javax.servlet.http.HttpServletResponse] [] (getOutputStream [] (proxy [javax.servlet.ServletOutputStream] [] (write ([b] (.write output-stream b)) ([b off len] (.write output-stream b off len))))) (setStatus [status] (swap! response assoc :status status)) (setHeader [name value] (swap! response assoc-in [:headers name] value)) (setCharacterEncoding [value]) (setContentType [value] (swap! response assoc :content-type value))))) (defn- servlet-config [] (proxy [javax.servlet.ServletConfig] [] (getServletContext [] nil))) (defn- run-servlet ([handler request response] (run-servlet handler request response {})) ([handler request response options] (doto (servlet handler options) (.init (servlet-config)) (.service (servlet-request request) (servlet-response response))))) (deftest make-service-method-test (let [handler (constantly {:status 201 :headers {}}) method (make-service-method handler) servlet (doto (proxy [javax.servlet.http.HttpServlet] []) (.init (servlet-config))) request {:server-port 8080 :server-name "foobar" :remote-addr "127.0.0.1" :uri "/foo" :scheme :http :request-method :get :protocol "HTTP/1.1" :headers {}} response (atom {})] (method servlet (servlet-request request) (servlet-response response)) (is (= (@response :status) 201)))) (deftest servlet-test (let [body (proxy [javax.servlet.ServletInputStream] []) cert (proxy [java.security.cert.X509Certificate] []) request {:server-port 8080 :server-name "foobar" :remote-addr "127.0.0.1" :uri "/foo" :query-string "a=b" :scheme :http :request-method :get :protocol "HTTP/1.1" :headers {"X-Client" ["Foo", "Bar"] "X-Server" ["Baz"] "X-Capital-I" ["Qux"]} :content-type "text/plain" :content-length 10 :character-encoding "UTF-8" :servlet-context-path "/foo" :ssl-client-cert cert :body body} response (atom {})] (letfn [(handler [r] (are [k v] (= (r k) v) :server-port 8080 :server-name "foobar" :remote-addr "127.0.0.1" :uri "/foo" :query-string "a=b" :scheme :http :request-method :get :protocol "HTTP/1.1" :headers {"x-client" "Foo,Bar" "x-server" "Baz" "x-capital-i" "Qux"} :content-type "text/plain" :content-length 10 :character-encoding "UTF-8" :servlet-context-path "/foo" :ssl-client-cert cert :body body) {:status 200, :headers {}})] (testing "request" (run-servlet handler request response)) (testing "mapping request header names to lower case" (with-locale (Locale. "tr") (run-servlet handler request response)))) (testing "response" (letfn [(handler [r] {:status 200 :headers {"Content-Type" "text/plain" "X-Server" "Bar"} :body "Hello World"})] (run-servlet handler request response) (is (= (@response :status) 200)) (is (= (@response :content-type) "text/plain")) (is (= (get-in @response [:headers "X-Server"]) "Bar")) (is (= (.toString (@response :body)) "Hello World")))))) (deftest servlet-cps-test (let [handler (fn [_ respond _] (respond {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"})) request {:completed (atom false) :server-port 8080 :server-name "foobar" :remote-addr "127.0.0.1" :uri "/foo" :scheme :http :request-method :get :protocol "HTTP/1.1" :headers {} :body nil} response (atom {})] (run-servlet handler request response {:async? true}) (is (= @(:completed request) true)) (is (= (@response :status) 200)) (is (= (@response :content-type) "text/plain")) (is (= (.toString (@response :body)) "Hello World")))) (defn- defservice-test* [service] (let [body (proxy [javax.servlet.ServletInputStream] []) servlet (doto (proxy [javax.servlet.http.HttpServlet] []) (.init (servlet-config))) request {:server-port 8080 :server-name "foobar" :remote-addr "127.0.0.1" :uri "/foo" :query-string "" :scheme :http :request-method :get :headers {} :content-type "text/plain" :content-length 10 :character-encoding "UTF-8" :body body} response (atom {})] (service servlet (servlet-request request) (servlet-response response)) (is (= (@response :status) 200)) (is (= (get-in @response [:headers "Content-Type" ]) "text/plain")) (is (= (.toString (@response :body)) "Hello World")))) (defn- service-handler [_] {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"}) (defservice "foo-" service-handler) (defservice service-handler {}) (deftest defservice-test (defservice-test* foo-service) (defservice-test* -service))