mirror of
https://github.com/metosin/reitit.git
synced 2025-12-20 01:21:11 +00:00
Merge a3c64baeba into f4da07c222
This commit is contained in:
commit
1cb0b31af8
2 changed files with 132 additions and 0 deletions
|
|
@ -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)})))})
|
||||
92
test/clj/reitit/ring/middleware/session_test.clj
Normal file
92
test/clj/reitit/ring/middleware/session_test.clj
Normal 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})))))))
|
||||
Loading…
Reference in a new issue