106 lines
3.2 KiB
Common Lisp
106 lines
3.2 KiB
Common Lisp
;;;; cl-scopes/web/response - set up and render server response
|
|
|
|
(defpackage :scopes/web/response
|
|
(:use :common-lisp)
|
|
(:local-nicknames (:actor :scopes/core/actor)
|
|
(:cookie :scopes/web/cookie)
|
|
(:core :scopes/core)
|
|
(:dom :scopes/web/dom)
|
|
(:message :scopes/core/message)
|
|
(:shape :scopes/shape)
|
|
(:util :scopes/util))
|
|
(:export #:setup #:html-response
|
|
#:render #:render-content #:render-not-found))
|
|
|
|
(in-package :scopes/web/response)
|
|
|
|
;;;; predefined action handlers / default actions
|
|
|
|
(defun render-msg (resp msg)
|
|
(prepare resp)
|
|
(if msg
|
|
(funcall (writer resp) (render-content resp msg)))
|
|
(finish resp))
|
|
|
|
(defun set-cookie (resp msg)
|
|
(util:plist-add (headers resp)
|
|
:set-cookie (render-cookie (shape:data msg)))
|
|
(render-msg resp nil))
|
|
|
|
(defvar *default-actions*
|
|
(core:define-actions '((:response :set-cookie) set-cookie)
|
|
'(nil render-msg)))
|
|
|
|
(defun render-cookie (cdata)
|
|
(let ((cookie (apply #'cookie:create-from-keys cdata)))
|
|
(cookie:make-header cookie)))
|
|
|
|
;;;; response definitions
|
|
|
|
(defvar *html-response-class* nil)
|
|
|
|
(defclass response (core:base-context actor:fg-actor)
|
|
((context :reader context :initarg :context)
|
|
(core:actions :initform *default-actions*)
|
|
(env :reader env :initarg :env)
|
|
(headers :accessor headers :initform nil)
|
|
(ctype :reader ctype :allocation :class)
|
|
(responder :accessor responder)
|
|
(writer :accessor writer)))
|
|
|
|
(defmethod print-object ((resp response) s)
|
|
(shape:print-fields resp s))
|
|
|
|
(defgeneric render-content (resp msg))
|
|
|
|
(defclass plain-text-response (response)
|
|
((ctype :initform "text/plain")))
|
|
|
|
(defclass json-response (response)
|
|
((ctype :initform "application/json")))
|
|
|
|
;;;; html response
|
|
|
|
(defclass html-response (response)
|
|
((ctype :initform "text/html")))
|
|
|
|
(defmethod render-content ((resp html-response) msg)
|
|
(dom:render (dom:dlist nil (shape:data msg))))
|
|
|
|
;;;; common definitions
|
|
|
|
(defun setup (ctx env &key html-responder)
|
|
(let* ((headers (getf env :headers))
|
|
(resp-class (select-response-class (gethash "accept" headers) html-responder)))
|
|
(actor:make-actor #'core:handle-message resp-class :context ctx :env env)))
|
|
|
|
(defun select-response-class (accept html-responder)
|
|
(let ((accept (string-downcase accept)))
|
|
(cond
|
|
((str:containsp "html" accept) (html-response-class html-responder))
|
|
((str:containsp "json" accept) 'json-response)
|
|
((str:containsp "plain" accept) 'plain-text-response)
|
|
(t (html-response-class html-responder)))))
|
|
|
|
(defun html-response-class (html-responder)
|
|
(or html-responder *html-response-class* 'html-response))
|
|
|
|
(defun make-headers (resp)
|
|
(cons :content-type (cons (ctype resp) (headers resp))))
|
|
|
|
(defun prepare (resp)
|
|
(let ((headers (make-headers resp))
|
|
(rcode 200))
|
|
(setf (writer resp) (funcall (responder resp) (list rcode headers)))))
|
|
|
|
(defun finish (resp)
|
|
(funcall (writer resp) nil :close t)
|
|
(actor:stop resp))
|
|
|
|
(defun render (resp)
|
|
#'(lambda (responder)
|
|
(setf (responder resp) responder)
|
|
(actor:start resp)))
|
|
|
|
(defun render-not-found(resp)
|
|
(list 404 '(:content-type "text/plain") '("Not found")))
|