Merge pull request #11 from fukamachi/efficient-session

Send Set-Cookie header only when it's necessary
This commit is contained in:
Eitaro Fukamachi 2015-09-26 15:26:37 +09:00
commit 8d6fe23e38
3 changed files with 51 additions and 11 deletions

View file

@ -30,7 +30,9 @@
(setf (getf env :lack.session) (setf (getf env :lack.session)
(or session (make-hash-table :test 'equal))) (or session (make-hash-table :test 'equal)))
(setf (getf env :lack.session.options) (setf (getf env :lack.session.options)
(list :id sid)) (if session
(list :id sid)
(list :id sid :new-session t)))
(finalize store state env (funcall app env))))) (finalize store state env (funcall app env)))))
"Middleware for session management") "Middleware for session management")

View file

@ -10,8 +10,6 @@
:make-response :make-response
:finalize-response :finalize-response
:response-set-cookies) :response-set-cookies)
(:import-from :alexandria
:remove-from-plist)
(:export :cookie-state (:export :cookie-state
:make-cookie-state :make-cookie-state
:generate-sid :generate-sid
@ -41,14 +39,20 @@
(funcall responder (finalize-state state sid actual-res options)))))) (funcall responder (finalize-state state sid actual-res options))))))
(defmethod finalize-state ((state cookie-state) sid (res list) options) (defmethod finalize-state ((state cookie-state) sid (res list) options)
;; Don't send Set-Cookie header when it's not necessary.
(destructuring-bind (&key no-store new-session change-id expire &allow-other-keys)
options
(when (or no-store
(not (or new-session change-id expire)))
(return-from finalize-state res)))
(let ((res (apply #'make-response res)) (let ((res (apply #'make-response res))
(options (append (remove-from-plist options :id) (options (with-slots (path domain expires secure httponly) state
(with-slots (path domain expires secure httponly) state (list :path path
(list :path path :domain domain
:domain domain :secure secure
:secure secure :httponly httponly
:httponly httponly :expires (+ (get-universal-time) expires)))))
:expires (+ (get-universal-time) expires))))))
(setf (getf (response-set-cookies res) :|lack.session|) (setf (getf (response-set-cookies res) :|lack.session|)
`(:value ,sid ,@options)) `(:value ,sid ,@options))
(finalize-response res))) (finalize-response res)))

View file

@ -6,7 +6,7 @@
:lack.test)) :lack.test))
(in-package :t.lack.middleware.session) (in-package :t.lack.middleware.session)
(plan 3) (plan 4)
(ok (lack.session.state:make-state) (ok (lack.session.state:make-state)
"Base class of session state") "Base class of session state")
@ -69,4 +69,38 @@
(is status 200) (is status 200)
(is body '("Hello, you've been here for 2th times!"))))))) (is body '("Hello, you've been here for 2th times!")))))))
(subtest "Set-Cookie header"
(let ((app (builder
:session
(lambda (env)
(when (string= (getf env :path-info) "/expire")
(setf (getf (getf env :lack.session.options) :expire) t))
'(200 () ("hi")))))
session)
;; 1st
(destructuring-bind (status headers body)
(funcall app (generate-env "/" :cookies '(("lack.session" . nil))))
(is status 200 "status")
(ok (getf headers :set-cookie)
"Set-Cookie header exists")
(setf session
(ppcre:scan-to-strings "(?<=lack.session=)[^;]+" (getf headers :set-cookie "")))
(is-type session 'string
"Set-Cookie header value is valid")
(is body '("hi") "body"))
;; 2nd
(destructuring-bind (status headers body)
(funcall app (generate-env "/" :cookies `(("lack.session" . ,session))))
(is status 200 "status")
(is (getf headers :set-cookie) nil
"Set-Cookie header doesn't exist")
(is body '("hi") "body"))
;; invalid lack.session
(destructuring-bind (status headers body)
(funcall app (generate-env "/" :cookies '(("lack.session" . "<invalid session here>"))))
(is status 200 "status")
(ok (getf headers :set-cookie)
"Set-Cookie header exists")
(is body '("hi") "body"))))
(finalize) (finalize)