diff --git a/doc/ring/static.md b/doc/ring/static.md index 4a679168..8be30562 100644 --- a/doc/ring/static.md +++ b/doc/ring/static.md @@ -54,15 +54,16 @@ This way, they are only served if none of the actual routes have matched. `reitit.ring/create-file-handler` and `reitit.ring/create-resource-handler` take optionally an options map to configure how the files are being served. -| key | description | -| -------------------|-------------| -| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` -| :root | optional resource root, defaults to `\"public\"` -| :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router. -| :loader | optional class loader to resolve the resources -| :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` -| :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly -| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found) +| key | description | +| --------------------|-------------| +| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` +| :root | optional resource root, defaults to `\"public\"` +| :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router. +| :loader | optional class loader to resolve the resources +| :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` +| :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly +| :canonicalize-uris? | optional boolean: if true (default), try to serve index files for non directory paths (paths that end with slash) +| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found) ### TODO diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 749b3074..6fb9ce6a 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -227,11 +227,12 @@ ;; TODO: ring.middleware.head/wrap-head ;; TODO: handle etags (defn -create-file-or-resource-handler - [response-fn {:keys [parameter root path loader allow-symlinks? index-files index-redirect? paths not-found-handler] + [response-fn {:keys [parameter root path loader allow-symlinks? index-files index-redirect? canonicalize-uris? paths not-found-handler] :or {parameter (keyword "") root "public" index-files ["index.html"] index-redirect? true + canonicalize-uris? true paths (constantly nil)}}] (let [options {:root root :loader loader @@ -254,13 +255,14 @@ (response/content-type response (mime-type/ext-mime-type path)))) path-or-index-response (fn [path uri] (or (response path) - (loop [[file & files] index-files] - (if file - (if-let [resp (response (join-paths path file))] - (if index-redirect? - (response/redirect (join-paths uri file)) - resp) - (recur files)))))) + (when (or canonicalize-uris? (str/ends-with? uri "/")) + (loop [[file & files] index-files] + (if file + (if-let [resp (response (join-paths path file))] + (if index-redirect? + (response/redirect (join-paths uri file)) + resp) + (recur files))))))) handler (if path (fn [request] (let [uri (impl/url-decode (:uri request))] @@ -278,15 +280,16 @@ (defn create-resource-handler "A ring handler for serving classpath resources, configured via options: - | key | description | - | -------------------|-------------| - | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` - | :root | optional resource root, defaults to `\"public\"` - | :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router. - | :loader | optional class loader to resolve the resources - | :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` - | :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly - | :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)" + | key | description | + | --------------------|-------------| + | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` + | :root | optional resource root, defaults to `\"public\"` + | :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router. + | :loader | optional class loader to resolve the resources + | :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` + | :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly + | :canonicalize-uris? | optional boolean: if true (default), try to serve index files for non directory paths (paths that end with slash) + | :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)" ([] (create-resource-handler nil)) ([opts] @@ -296,15 +299,16 @@ (defn create-file-handler "A ring handler for serving file resources, configured via options: - | key | description | - | -------------------|-------------| - | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` - | :root | optional resource root, defaults to `\"public\"` - | :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router. - | :loader | optional class loader to resolve the resources - | :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` - | :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly - | :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)" + | key | description | + | --------------------|-------------| + | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` + | :root | optional resource root, defaults to `\"public\"` + | :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router. + | :loader | optional class loader to resolve the resources + | :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` + | :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly + | :canonicalize-uris? | optional boolean: if true (default), try to serve index files for non directory paths (paths that end with slash) + | :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)" ([] (create-file-handler nil)) ([opts] diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index d6a6fd2e..57a11db6 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -697,7 +697,31 @@ (is (= "

hello

\n" (slurp (:body response))))) (let [response (app (request "/docs/"))] (is (= 200 (:status response))) - (is (= "

hello

\n" (slurp (:body response))))))))))))) + (is (= "

hello

\n" (slurp (:body response)))))))) + + (testing "with canonicalize-uris" + (let [app (ring/ring-handler + (ring/router + ["/*" (create {:canonicalize-uris? true})]) + (ring/create-default-handler))] + + (testing "index-files" + (let [response (app (request "/docs"))] + (is (= (redirect "/docs/index.html") response))) + (let [response (app (request "/docs/"))] + (is (= (redirect "/docs/index.html") response)))))) + + (testing "without canonicalize-uris" + (let [app (ring/ring-handler + (ring/router + ["/*" (create {:canonicalize-uris? false})]) + (ring/create-default-handler))] + + (testing "index-files" + (let [response (app (request "/docs"))] + (is (= 404 (:status response)))) + (let [response (app (request "/docs/"))] + (is (= (redirect "/docs/index.html") response))))))))))) #?(:clj (deftest file-resource-handler-not-found-test