Permalink
Please sign in to comment.
Showing
with
330 additions
and 340 deletions.
- +1 −1 .gitignore
- +47 −38 README.textile
- +3 −3 SPEC
- +29 −30 src/ring/{ → adapter}/httpcore.clj
- +3 −3 src/ring/{ → adapter}/jetty.clj
- +0 −8 src/ring/builder.clj
- +4 −4 src/ring/{examples → example}/hello_world.clj
- +21 −0 src/ring/example/linted.clj
- 0 src/ring/{examples → example}/public/clojure.png
- +22 −0 src/ring/example/wrapping.clj
- +0 −19 src/ring/examples/linted.clj
- +0 −16 src/ring/examples/wrapping.clj
- +0 −47 src/ring/file.clj
- +4 −4 src/ring/{ → handler}/dump.clj
- +60 −0 src/ring/middleware/file.clj
- +16 −18 src/ring/{ → middleware}/file_info.clj
- +4 −3 src/ring/{ → middleware}/lint.clj
- +3 −3 src/ring/{ → middleware}/reload.clj
- +5 −6 src/ring/{backtrace.clj → middleware/stacktrace.clj}
- +17 −0 src/ring/middleware/static.clj
- +0 −18 src/ring/static.clj
- +0 −25 src/ring/utils.clj
- +0 −10 test/ring/builder_test.clj
- +0 −35 test/ring/file_info_test.clj
- +7 −6 test/ring/{ → handler}/dump_test.clj
- +38 −0 test/ring/middleware/file_info_test.clj
- +14 −13 test/ring/{ → middleware}/file_test.clj
- +5 −5 test/ring/{ → middleware}/lint_test.clj
- +9 −0 test/ring/middleware/reload_test.clj
- +5 −4 test/ring/{backtrace_test.clj → middleware/stacktrace_test.clj}
- +6 −5 test/ring/{ → middleware}/static_test.clj
- +0 −8 test/ring/reload_test.clj
- +7 −8 test/run.clj
2
.gitignore
| @@ -1 +1 @@ | ||
| -bundle | ||
| +bundle |
85
README.textile
6
SPEC
59
src/ring/httpcore.clj → src/ring/adapter/httpcore.clj
6
src/ring/jetty.clj → src/ring/adapter/jetty.clj
8
src/ring/builder.clj
| @@ -1,8 +0,0 @@ | ||
| -(ns ring.builder) | ||
| - | ||
| -(defn wrap-if | ||
| - "If test is logically true, returns the result of invoking the wrapper on the | ||
| - core app, i.e. a wrapped app; if test is logically false, returns the core | ||
| - app." | ||
| - [test wrapper-fn core-app] | ||
| - (if test (wrapper-fn core-app) core-app)) |
8
src/ring/examples/hello_world.clj → src/ring/example/hello_world.clj
21
src/ring/example/linted.clj
| @@ -0,0 +1,21 @@ | ||
| +; An example of inserting the linter between each component to ensure | ||
| +; compliance to the Ring spec. | ||
| + | ||
| +(ns ring.examples.linted | ||
| + (:use (ring.handler dump) | ||
| + (ring.middleware stacktrace file file-info reload lint) | ||
| + (ring.adapter jetty))) | ||
| + | ||
| +(def app | ||
| + (-> handle-dump | ||
| + wrap-lint | ||
| + wrap-stacktrace | ||
| + wrap-lint | ||
| + wrap-file-info | ||
| + wrap-lint | ||
| + (wrap-file "src/ring/examples/public") | ||
| + wrap-lint | ||
| + (wrap-reload '(ring.dump) | ||
| + wrap-lint))) | ||
| + | ||
| +(run-jetty app {:port 8080}) |
0
src/ring/examples/public/clojure.png → src/ring/example/public/clojure.png
File renamed without changes
22
src/ring/example/wrapping.clj
| @@ -0,0 +1,22 @@ | ||
| +; A example of modular construction of Ring apps. | ||
| + | ||
| +(ns ring.example.wrapping | ||
| + (:use (ring.handler dump) | ||
| + (ring.middleware stacktrace file-info file) | ||
| + (ring.adapter jetty) | ||
| + (clojure.contrib fcase))) | ||
| + | ||
| +(defn wrap-error [app] | ||
| + (fn [req] | ||
| + (if (= "/error" (:uri req)) | ||
| + (throwf "Demonstrating ring.middleware.stacktrace") | ||
| + (app req)))) | ||
| + | ||
| +(def app | ||
| + (-> handle-dump | ||
| + wrap-stacktrace | ||
| + wrap-file-info | ||
| + (wrap-file "src/ring/examples/public") | ||
| + wrap-error)) | ||
| + | ||
| +(run-jetty app {:port 8080}) |
19
src/ring/examples/linted.clj
| @@ -1,19 +0,0 @@ | ||
| -; An example of inserting the linter inserted each component to ensure | ||
| -; compliance to the Ring spec. | ||
| - | ||
| -(ns ring.examples.linted | ||
| - (:require (ring backtrace file file-info reload lint dump jetty)) | ||
| - (:import (java.io File))) | ||
| - | ||
| -(def app | ||
| - (ring.lint/wrap | ||
| - (ring.backtrace/wrap | ||
| - (ring.lint/wrap | ||
| - (ring.file-info/wrap | ||
| - (ring.lint/wrap | ||
| - (ring.file/wrap (File. "src/ring/examples/public") | ||
| - (ring.lint/wrap | ||
| - (ring.reload/wrap '(ring.dump) | ||
| - ring.dump/app))))))))) | ||
| - | ||
| -(ring.jetty/run {:port 8080} app) |
16
src/ring/examples/wrapping.clj
| @@ -1,16 +0,0 @@ | ||
| -; A example of modular construction of Ring apps. | ||
| - | ||
| -(ns ring.examples.wrapping | ||
| - (:require (ring backtrace file-info file dump jetty)) | ||
| - (:import (java.io File))) | ||
| - | ||
| -(def app | ||
| - (ring.backtrace/wrap | ||
| - (ring.file-info/wrap | ||
| - (ring.file/wrap (File. "src/ring/examples/public") | ||
| - (fn [req] | ||
| - (if (= "/error" (:uri req)) | ||
| - (throw (Exception. "Demonstrating ring.backtrace")) | ||
| - (ring.dump/app req))))))) | ||
| - | ||
| -(ring.jetty/run {:port 8080} app) |
47
src/ring/file.clj
| @@ -1,47 +0,0 @@ | ||
| -(ns ring.file | ||
| - (:use clojure.contrib.except | ||
| - ring.utils) | ||
| - (:import (java.io File))) | ||
| - | ||
| -(defn- ensure-dir | ||
| - "Ensures that the given directory exists." | ||
| - [dir] | ||
| - (throw-if-not (.exists dir) | ||
| - "Directory does not exist: %s" dir)) | ||
| - | ||
| -(defn- forbidden | ||
| - [] | ||
| - {:status 403 | ||
| - :headers {"Content-Type" "text/html"} | ||
| - :body "<html><body><h1>403 Forbidden</h1></body></html>"}) | ||
| - | ||
| -(defn- success | ||
| - [file] | ||
| - {:status 200 :headers {} :body file}) | ||
| - | ||
| -(defn- maybe-file | ||
| - "Returns the File corresponding to the given relative path within the given | ||
| - dir if it exists, or nil if no such file exists." | ||
| - [dir path] | ||
| - (let [file (File. dir path)] | ||
| - (and (.exists file) (.canRead file) file))) | ||
| - | ||
| -(defn wrap | ||
| - "Wrap an app such that a given directory is checked for a static file | ||
| - with which to respond to the request, proxying the request to the | ||
| - wrapped app if such a file does not exist." | ||
| - [dir app] | ||
| - (ensure-dir dir) | ||
| - (fn [req] | ||
| - (if (#{:get :head} (:request-method req)) | ||
| - (let [uri (url-decode (:uri req))] | ||
| - (if (str-includes? ".." uri) | ||
| - (forbidden) | ||
| - (let [path (cond | ||
| - (.endsWith "/" uri) (str uri "index.html") | ||
| - (re-match? #"\.[a-z]+$" uri) uri | ||
| - :else (str uri ".html"))] | ||
| - (if-let [file (maybe-file dir path)] | ||
| - (success file) | ||
| - (app req))))) | ||
| - (app req)))) |
8
src/ring/dump.clj → src/ring/handler/dump.clj
60
src/ring/middleware/file.clj
| @@ -0,0 +1,60 @@ | ||
| +(ns ring.middleware.file | ||
| + (:use clojure.contrib.except) | ||
| + (:import (java.io File))) | ||
| + | ||
| +(defn- url-decode | ||
| + "Returns the form-url-decoded version of the given string." | ||
| + [encoded] | ||
| + (java.net.URLDecoder/decode encoded "UTF-8")) | ||
| + | ||
| +(defn- str-includes? | ||
| + "Returns logical truth iff the given target appears in the given string" | ||
| + [target string] | ||
| + (<= 0 (.indexOf string target))) | ||
| + | ||
| +(defn- ensure-dir | ||
| + "Ensures that the given directory exists and returns it, throwing if it does | ||
| + not. If the directory is given as a String, it is coerced to a Fil before | ||
| + returning." | ||
| + [dir] | ||
| + (let [fdir (if (string? dir) (File. dir) dir)] | ||
| + (throw-if-not (.exists fdir) | ||
| + "Directory does not exist: %s" fdir) | ||
| + fdir)) | ||
| + | ||
| +(defn- forbidden | ||
| + [] | ||
| + {:status 403 | ||
| + :headers {"Content-Type" "text/html"} | ||
| + :body "<html><body><h1>403 Forbidden</h1></body></html>"}) | ||
| + | ||
| +(defn- success | ||
| + [file] | ||
| + {:status 200 :headers {} :body file}) | ||
| + | ||
| +(defn- maybe-file | ||
| + "Returns the File corresponding to the given relative path within the given | ||
| + dir if it exists, or nil if no such file exists." | ||
| + [dir path] | ||
| + (let [file (File. dir path)] | ||
| + (and (.exists file) (.canRead file) file))) | ||
| + | ||
| +(defn wrap-file | ||
| + "Wrap an app such that a given directory is checked for a static file | ||
| + with which to respond to the request, proxying the request to the | ||
| + wrapped app if such a file does not exist." | ||
| + [app dir] | ||
| + (let [fdir (ensure-dir dir)] | ||
| + (fn [req] | ||
| + (if (#{:get :head} (:request-method req)) | ||
| + (let [uri (url-decode (:uri req))] | ||
| + (if (str-includes? ".." uri) | ||
| + (forbidden) | ||
| + (let [path (cond | ||
| + (.endsWith "/" uri) (str uri "index.html") | ||
| + (re-find #"\.[a-z]+$" uri) uri | ||
| + :else (str uri ".html"))] | ||
| + (if-let [file (maybe-file fdir path)] | ||
| + (success file) | ||
| + (app req))))) | ||
| + (app req))))) |
34
src/ring/file_info.clj → src/ring/middleware/file_info.clj
7
src/ring/lint.clj → src/ring/middleware/lint.clj
6
src/ring/reload.clj → src/ring/middleware/reload.clj
11
src/ring/backtrace.clj → src/ring/middleware/stacktrace.clj
17
src/ring/middleware/static.clj
| @@ -0,0 +1,17 @@ | ||
| +(ns ring.middleware.static | ||
| + (:use (ring.middleware file))) | ||
| + | ||
| +(defn wrap-static | ||
| + "Like ring.file, but takes an additional statics, a coll of Strings that will | ||
| + be used to test incoming requests uris. If a uri begins with any of the | ||
| + strings in the statics coll, the middleware will check to see if a file can be | ||
| + served from the public-dir before proxying back to the given app; if the uri | ||
| + does not correspond to one of these strings, the middleware proxies the | ||
| + request directly back to the app without touching the filesystem." | ||
| + [app public-dir statics] | ||
| + (let [app-with-file (wrap-file app public-dir)] | ||
| + (fn [req] | ||
| + (let [uri (:uri req)] | ||
| + (if (some #(.startsWith uri %) statics) | ||
| + (app-with-file req) | ||
| + (app req)))))) |
18
src/ring/static.clj
| @@ -1,18 +0,0 @@ | ||
| -(ns ring.static | ||
| - (:require ring.file) | ||
| - (:use ring.utils)) | ||
| - | ||
| -(defn wrap | ||
| - "Like ring.file, but takes an additional statics, a coll of Strings that will | ||
| - be used to test incoming requests uris. If the uri begins with any of the | ||
| - strings in statics, the middleware will check to see if a files can be served | ||
| - from the public-dir before proxying back to the given app; if the uri does not | ||
| - match the re, proxies the request directly back to the app without touching the | ||
| - filesystem." | ||
| - [public-dir statics app] | ||
| - (let [app-with-file (ring.file/wrap public-dir app)] | ||
| - (fn [req] | ||
| - (let [uri (:uri req)] | ||
| - (if (some #(.startsWith uri %) statics) | ||
| - (app-with-file req) | ||
| - (app req)))))) |
25
src/ring/utils.clj
| @@ -1,25 +0,0 @@ | ||
| -(ns ring.utils | ||
| - (:use clojure.contrib.str-utils)) | ||
| - | ||
| -(defn url-decode | ||
| - "Returns the form-url-decoded version of the given string." | ||
| - [encoded] | ||
| - (java.net.URLDecoder/decode encoded "UTF-8")) | ||
| - | ||
| -(defn str-includes? | ||
| - "Returns logical truth iff the given target appears in the given string" | ||
| - [target string] | ||
| - (<= 0 (.indexOf string target))) | ||
| - | ||
| -(defn re-match? | ||
| - "Returns true iff the given string contains a match for the given pattern." | ||
| - [#^java.util.regex.Pattern pattern string] | ||
| - (.find (.matcher pattern string))) | ||
| - | ||
| -(defn re-get | ||
| - "Returns the nth captured group resulting from matching the given pattern | ||
| - against the given string, or nil if no match is found." | ||
| - [re n s] | ||
| - (let [m (re-matcher re s)] | ||
| - (if (.find m) | ||
| - (.group m n)))) |
10
test/ring/builder_test.clj
| @@ -1,10 +0,0 @@ | ||
| -(ns ring.builder-test | ||
| - (:use clj-unit.core ring.builder)) | ||
| - | ||
| -(deftest "wrap-if" | ||
| - (let [core-app inc | ||
| - wrapper-fn (fn [app] (fn [req] (+ 2 (app req)))) | ||
| - unwrapped (wrap-if false wrapper-fn core-app) | ||
| - wrapped (wrap-if true wrapper-fn core-app)] | ||
| - (assert= 1 (unwrapped 0)) | ||
| - (assert= 3 (wrapped 0)))) |
35
test/ring/file_info_test.clj
| @@ -1,35 +0,0 @@ | ||
| -(ns ring.file-info-test | ||
| - (:use clj-unit.core ring.file-info) | ||
| - (:import java.io.File)) | ||
| - | ||
| -(def non-file-app (wrap (constantly {:headers {} :body "body"}))) | ||
| - | ||
| -(def known-file (File. "test/ring/assets/plain.txt")) | ||
| -(def known-file-app (wrap (constantly {:headers {} :body known-file}))) | ||
| - | ||
| -(def unknown-file (File. "test/ring/assets/random.xyz")) | ||
| -(def unknown-file-app (wrap (constantly {:headers {} :body unknown-file}))) | ||
| - | ||
| -(def custom-type-app (wrap {"txt" "custom/type"} | ||
| - (constantly {:headers {} :body known-file}))) | ||
| - | ||
| -(deftest "wrap: non-file response" | ||
| - (assert= {:headers {} :body "body"} (non-file-app {}))) | ||
| - | ||
| -(deftest "wrap: known file response" | ||
| - (assert= | ||
| - {:headers {"Content-Type" "text/plain" "Content-Length" "6"} | ||
| - :body known-file} | ||
| - (known-file-app {}))) | ||
| - | ||
| -(deftest "wrap: unknown file resonse" | ||
| - (assert= | ||
| - {:headers {"Content-Type" "application/octet-stream" "Content-Length" "7"} | ||
| - :body unknown-file} | ||
| - (unknown-file-app {}))) | ||
| - | ||
| -(deftest "wrap: custom mime types" | ||
| - (assert= | ||
| - {:headers {"Content-Type" "custom/type" "Content-Length" "6"} | ||
| - :body known-file} | ||
| - (custom-type-app {}))) |
13
test/ring/dump_test.clj → test/ring/handler/dump_test.clj
38
test/ring/middleware/file_info_test.clj
| @@ -0,0 +1,38 @@ | ||
| +(ns ring.middleware.file-info-test | ||
| + (:use (clj-unit core) | ||
| + (ring.middleware file-info)) | ||
| + (: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}))) | ||
| + | ||
| +(def custom-type-app | ||
| + (wrap-file-info | ||
| + (constantly {:headers {} :body known-file}) | ||
| + {"txt" "custom/type"})) | ||
| + | ||
| +(deftest "wrap-file-info: non-file response" | ||
| + (assert= {:headers {} :body "body"} (non-file-app {}))) | ||
| + | ||
| +(deftest "wrap-file-info: known file response" | ||
| + (assert= | ||
| + {:headers {"Content-Type" "text/plain" "Content-Length" "6"} | ||
| + :body known-file} | ||
| + (known-file-app {}))) | ||
| + | ||
| +(deftest "wrap-file-info: unknown file resonse" | ||
| + (assert= | ||
| + {:headers {"Content-Type" "application/octet-stream" "Content-Length" "7"} | ||
| + :body unknown-file} | ||
| + (unknown-file-app {}))) | ||
| + | ||
| +(deftest "wrap-file-info: custom mime types" | ||
| + (assert= | ||
| + {:headers {"Content-Type" "custom/type" "Content-Length" "6"} | ||
| + :body known-file} | ||
| + (custom-type-app {}))) |
27
test/ring/file_test.clj → test/ring/middleware/file_test.clj
| @@ -1,42 +1,43 @@ | ||
| -(ns ring.file-test | ||
| - (:use clj-unit.core ring.file) | ||
| - (:import java.io.File)) | ||
| +(ns ring.middleware.file-test | ||
| + (:use (clj-unit core) | ||
| + (ring.middleware file)) | ||
| + (:import (java.io File))) | ||
| -(deftest "wrap: no directory" | ||
| +(deftest "wrap-file: no directory" | ||
| (assert-throws #"Directory does not exist" | ||
| - (wrap (File. "not_here") (constantly :response)))) | ||
| + (wrap-file (constantly :response) (File. "not_here")))) | ||
| -(def public-dir (File. "test/ring/assets")) | ||
| +(def public-dir "test/ring/assets") | ||
| (def index-html (File. public-dir "index.html")) | ||
| (def foo-html (File. public-dir "foo.html")) | ||
| -(def app (wrap public-dir (constantly :response))) | ||
| +(def app (wrap-file (constantly :response) public-dir)) | ||
| -(deftest "wrap: unsafe method" | ||
| +(deftest "wrap-file: unsafe method" | ||
| (assert= :response (app {:request-method :post :uri "/foo"}))) | ||
| -(deftest "wrap: forbidden url" | ||
| +(deftest "wrap-file: forbidden url" | ||
| (let [{:keys [status body]} (app {:request-method :get :uri "/../foo"})] | ||
| (assert= 403 status) | ||
| (assert-match #"Forbidden" body))) | ||
| -(deftest "wrap: directory" | ||
| +(deftest "wrap-file: directory" | ||
| (let [{:keys [status headers body]} (app {:request-method :get :uri "/"})] | ||
| (assert= 200 status) | ||
| (assert= {} headers) | ||
| (assert= index-html body))) | ||
| -(deftest "wrap: file without extension" | ||
| +(deftest "wrap-file: file without extension" | ||
| (let [{:keys [status headers body]} (app {:request-method :get :uri "/foo"})] | ||
| (assert= 200 status) | ||
| (assert= {} headers) | ||
| (assert= foo-html body))) | ||
| -(deftest "wrap: file with extension" | ||
| +(deftest "wrap-file: file with extension" | ||
| (let [{:keys [status headers body]} (app {:request-method :get :uri "/foo.html"})] | ||
| (assert= 200 status) | ||
| (assert= {} headers) | ||
| (assert= foo-html body))) | ||
| -(deftest "wrap: no file" | ||
| +(deftest "wrap-file: no file" | ||
| (assert= :response (app {:request-method :get :uri "/dynamic"}))) |
10
test/ring/lint_test.clj → test/ring/middleware/lint_test.clj
9
test/ring/middleware/reload_test.clj
| @@ -0,0 +1,9 @@ | ||
| +(ns ring.middleware.reload-test | ||
| + (:use (clj-unit core) | ||
| + (ring.middleware reload))) | ||
| + | ||
| +(def app | ||
| + (wrap-reload (constantly :response) '(ring.middleware.reload))) | ||
| + | ||
| +(deftest "wrap-reload" | ||
| + (assert= :response (app :request))) |
9
test/ring/backtrace_test.clj → test/ring/middleware/stacktrace_test.clj
11
test/ring/static_test.clj → test/ring/middleware/static_test.clj
| @@ -1,18 +1,19 @@ | ||
| -(ns ring.static-test | ||
| - (:use clj-unit.core ring.static) | ||
| - (:import java.io.File)) | ||
| +(ns ring.middleware.static-test | ||
| + (:use (clj-unit core) | ||
| + (ring.middleware static)) | ||
| + (:import (java.io File))) | ||
| (def public-dir (File. "test/ring/assets")) | ||
| (def foo-html (File. "test/ring/assets/foo.html")) | ||
| (def nested-foo-html (File. "test/ring/assets/bars/foo.html")) | ||
| (def statics ["/foo.html" "/bars/"]) | ||
| -(def app (ring.static/wrap public-dir statics (constantly {:body :dynamic}))) | ||
| +(def app (wrap-static (constantly {:body :dynamic}) public-dir statics)) | ||
| (defn app-response-body [uri] | ||
| (:body (app {:request-method :get :uri uri}))) | ||
| -(deftest "wrap" | ||
| +(deftest "wrap-static" | ||
| (assert= foo-html (app-response-body "/foo.html")) | ||
| (assert= nested-foo-html (app-response-body "/bars/foo.html")) | ||
| (assert= :dynamic (app-response-body "/not/static"))) |
8
test/ring/reload_test.clj
| @@ -1,8 +0,0 @@ | ||
| -(ns ring.reload-test | ||
| - (:use clj-unit.core ring.reload)) | ||
| - | ||
| -(def app (constantly :response)) | ||
| - | ||
| -(deftest "wrap" | ||
| - (let [wrapped (wrap '(ring.reload) app)] | ||
| - (assert= :response (wrapped :request)))) |
15
test/run.clj
| @@ -1,10 +1,9 @@ | ||
| (use 'clj-unit.core) | ||
| (require-and-run-tests | ||
| - 'ring.builder-test | ||
| - 'ring.dump-test | ||
| - 'ring.lint-test | ||
| - 'ring.file-test | ||
| - 'ring.file-info-test | ||
| - 'ring.static-test | ||
| - 'ring.reload-test | ||
| - 'ring.backtrace-test) | ||
| + 'ring.handler.dump-test | ||
| + 'ring.middleware.lint-test | ||
| + 'ring.middleware.file-test | ||
| + 'ring.middleware.file-info-test | ||
| + 'ring.middleware.static-test | ||
| + 'ring.middleware.reload-test | ||
| + 'ring.middleware.stacktrace-test) |
0 comments on commit
cbeebac