Compare commits

...

7 commits

Author SHA1 Message Date
Daw-Ran Liou
c953d9c82a
Merge a3c64baeba into 7520d20f12 2025-06-24 09:07:11 -05:00
Daw-Ran Liou
a3c64baeba Change session middleware's default to off
When `:session` key is absent in the route data, the session middleware will not
be attached to the route. To enable the middleware, the user at least need to
use an empty map `{}` for the `:session`, which uses the default options.
2019-10-04 11:00:20 -07:00
Daw-Ran Liou
99efdeea2d Remove .nrepl-port
Recursively ignore .nrepl-port in the sub-directories.
2019-10-03 07:30:39 -07:00
Daw-Ran Liou
f46244cc48 Fix spec and docstring
Document specs for the :session entity map.
Fix the default session options.
Add test to validate spec.
2019-10-01 14:09:27 -07:00
Daw-Ran Liou
78a1cc144e Add spec 2019-10-01 07:18:19 -07:00
Daw-Ran Liou
888856b5cc Add tests for session middleware 2019-09-30 16:30:52 -07:00
Daw-Ran Liou
83b2e90ca7 Add reitit.ring.middleware.session ns 2019-09-30 15:48:08 -07:00
2 changed files with 132 additions and 0 deletions

View file

@ -0,0 +1,40 @@
(ns reitit.ring.middleware.session
(:require
[clojure.spec.alpha :as s]
[ring.middleware.session :as session]
[ring.middleware.session.store :as session-store]
[ring.middleware.session.memory :as memory]))
(s/def ::store #(satisfies? session-store/SessionStore %))
(s/def ::root string?)
(s/def ::cookie-name string?)
(s/def ::cookie-attrs map?)
(s/def ::session (s/keys :opt-un [::store ::root ::cookie-name ::cookie-attrs]))
(s/def ::spec (s/keys :opt-un [::session]))
(def ^:private store
"The default shared in-memory session store.
This is used when no `:store` key is provided to the middleware."
(memory/memory-store (atom {})))
(def session-middleware
"Middleware for session.
Enter:
Add the `:session` key into the request map based on the `:cookies`
in the request map.
Exit:
When `:session` key presents in the response map, update the session
store with its value. Then remove `:session` from the response map.
| key | description |
| -------------|-------------|
| `:session` | A map of options that passes into the [`ring.middleware.session/wrap-session](http://ring-clojure.github.io/ring/ring.middleware.session.html#var-wrap-session) function`, or an empty map for the default options. The absence of this value will disable the middleware."
{:name :session
:spec ::spec
:compile (fn [{session-opts :session} _]
(if session-opts
(let [session-opts (merge {:store store} session-opts)]
{:wrap #(session/wrap-session % session-opts)})))})

View file

@ -0,0 +1,92 @@
(ns reitit.ring.middleware.session-test
(:require [clojure.test :refer [deftest testing is]]
[reitit.ring.middleware.session :as session]
[ring.middleware.session.memory :as memory]
[reitit.spec :as rs]
[reitit.ring :as ring]
[reitit.ring.spec :as rrs]))
(defn get-session-id
"Parse the session-id out of response headers."
[request]
(let [pattern #"ring-session=([-\w]+);Path=/;HttpOnly"
parse-fn (partial re-find pattern)]
(some-> request
(get-in [:headers "Set-Cookie"])
first
parse-fn
second)))
(defn handler
"The handler that increments the counter."
[{session :session}]
(let [counter (inc (:counter session 0))]
{:status 200
:body {:counter counter}
:session {:counter counter}}))
(deftest session-test
(testing "Custom session store"
(let [store (atom {})
app (ring/ring-handler
(ring/router
["/api"
{:session {:store (memory/memory-store store)}
:middleware [session/session-middleware]}
["/ping" handler]
["/pong" handler]]))
first-response (app {:request-method :get
:uri "/api/ping"})
session-id (get-session-id first-response)
second-response (app {:request-method :get
:uri "/api/pong"
:cookies {"ring-session" {:value session-id}}})]
(testing "shared across routes"
(is (= (count @store)
1))
(is (-> @store first second)
{:counter 2})))))
(deftest default-session-test
(testing "Default session store"
(let [app (ring/ring-handler
(ring/router
["/api"
{:middleware [session/session-middleware]
:session {}}
["/ping" handler]
["/pong" handler]]))
first-response (app {:request-method :get
:uri "/api/ping"})
session-id (get-session-id first-response)
second-response (app {:request-method :get
:uri "/api/pong"
:cookies {"ring-session" {:value session-id}}})]
(testing "shared across routes"
(is (= (inc (get-in first-response [:body :counter]))
(get-in second-response [:body :counter])))))))
(deftest default-session-off-test
(testing "Default session middleware"
(let [app (ring/ring-handler
(ring/router
["/api"
{:middleware [session/session-middleware]}
["/ping" handler]]))
resp (app {:request-method :get
:uri "/api/ping"})]
(testing "off by default"
(is (nil? (get-session-id resp)))))))
(deftest session-spec-test
(testing "Session spec"
(testing "with invalid session store type"
(is
(thrown? Exception
(ring/ring-handler
(ring/router
["/api"
{:session {:store nil}
:middleware [session/session-middleware]
:handler handler}]
{:validate rrs/validate})))))))