;;;; Programmer: Kevin M. Rosenberg
;;;; Date Started: Dec 2002
;;;;
-;;;; $Id: utils.lisp,v 1.6 2003/07/11 02:38:00 kevin Exp $
+;;;; $Id$
;;;; *************************************************************************
(in-package #:modlisp)
-(defun format-string (fmt)
+(defun format->string (fmt)
(case fmt
(:html "text/html")
(:xml "text/xml")
(:text "text/plain")
(otherwise fmt)))
-
-(defmacro with-ml-page ((&key (format :html) (precompute t)) &body body)
- (let ((fmt (gensym))
- (precomp (gensym))
- (result (gensym))
- (outstr (gensym))
- (stream (gensym)))
- `(let ((,fmt ,format)
- (,precomp ,precompute)
- ,result ,outstr)
- (write-header-line "Status" "200 OK")
- (write-header-line "Content-Type" (format-string ,fmt))
- (unless ,precomp
- (write-string "end" *modlisp-socket*)
- (write-char #\NewLine *modlisp-socket*))
- (setq ,outstr
- (with-output-to-string (,stream)
- (let ((*modlisp-socket* (if ,precomp
- ,stream
- *modlisp-socket*)))
- (setq ,result (progn ,@body)))))
- (cond
- (,precomp
- (write-header-line "Content-Length"
- (write-to-string (length ,outstr)))
- (write-header-line "Keep-Socket" "1")
- (write-header-line "Keep-Alive" "timeout=15, max=99")
- (write-header-line "Connection" "Keep-Alive")
- (write-string "end" *modlisp-socket*)
- (write-char #\NewLine *modlisp-socket*)
- (write-string ,outstr *modlisp-socket*)
- (finish-output *modlisp-socket*)
- (setq *close-modlisp-socket* nil))
- (t
- (setq *close-modlisp-socket* t)
- (finish-output *modlisp-socket*)))
- ,result)))
+
+(defun format-string (fmt headers)
+ `(("Content-Type" .
+ ,(format->string fmt))
+ . ,headers))
+
+(defmacro write-response ((&key headers len (status "200 OK")) &body body)
+ (let ((result (gensym "RES-")))
+ `(progn
+ (write-header-line "Status" ,status)
+ (dolist (hdr ,headers)
+ (write-header-line (car hdr) (cdr hdr)))
+ ,@(and len
+ `((write-header-line "Content-Length" ,len)
+ (write-header-line "Keep-Socket" "1")
+ (write-header-line "Connection" "Keep-Alive")))
+ (write-string "end" *modlisp-socket*)
+ (write-char #\NewLine *modlisp-socket*)
+ (let ((,result (progn ,@body)))
+ (,(if len 'force-output 'finish-output) *modlisp-socket*)
+ (setq *close-modlisp-socket* ,(not len))
+ ,result))))
+
+(defmacro with-ml-page ((&key (format :html) (precompute t) headers)
+ &body body)
+ (if precompute
+ `(output-ml-page ,format (with-output-to-string (*modlisp-socket*) ,@body) :headers ,headers)
+ `(write-response (:headers (format-string ,format ,headers)) ,@body)))
(defun redirect-to-location (url)
- (write-header-line "Status" "302 Redirect")
- (write-header-line "Location" url)
- (write-char #\NewLine *modlisp-socket*)
- (setq *close-modlisp-socket* t))
+ (write-response (:status "307 Temporary Redirect" :headers `(("Location" . ,url)))))
-(defun output-ml-page (format html)
- (write-header-line "Status" "200 OK")
- (write-header-line "Content-Type" (format-string format))
- (write-header-line "Content-Length" (format nil "~d" (length html)))
- (write-header-line "Keep-Socket" "1")
- (write-header-line "Keep-Alive" "timeout=15, max=99")
- (write-header-line "Connection" "Keep-Alive")
- (write-string "end" *modlisp-socket*)
- (write-char #\NewLine *modlisp-socket*)
- (write-string html *modlisp-socket*)
- (setq *close-modlisp-socket* nil))
+(defmacro output-ml-page (format html &key headers)
+ (let ((str (gensym "STR-")))
+ `(let ((,str ,html))
+ (write-response (:len (format nil "~d" (length ,str))
+ :headers (format-string ,format ,headers))
+ (write-string ,str *modlisp-socket*)))))
-(defun output-html-page (str)
- (output-ml-page :html str))
+(defun output-html-page (str &key headers)
+ (output-ml-page :html str :headers headers))
-(defun output-xml-page (str)
- (output-ml-page :xml str))
+(defun output-xml-page (str &key headers)
+ (output-ml-page :xml str :headers headers))
;; Utility functions for library users
-(defun posted-to-alist (posted-string)
+(defun query-to-alist (posted-string &key (keyword t))
"Converts a posted string to an assoc list of keyword names and values,
\"a=1&bc=demo\" => ((:a . \"1\") (:bc . \"demo\"))"
(when posted-string
(let ((alist '()))
(dolist (name=val (kmrcl:delimited-string-to-list posted-string #\&)
- (nreverse alist))
- (let ((name-val-list (kmrcl:delimited-string-to-list name=val #\=)))
- (if (= 2 (length name-val-list))
- (destructuring-bind (name val) name-val-list
- (push (cons (kmrcl:ensure-keyword name)
- (kmrcl:decode-uri-query-string val))
- alist))
- (cmsg-c :debug "Invalid number of #\= in ~S" name-val-list)))))))
+ (nreverse alist))
+ (let ((name-val-list (kmrcl:delimited-string-to-list name=val #\=)))
+ (if (= 2 (length name-val-list))
+ (destructuring-bind (name val) name-val-list
+ (push (cons (if keyword
+ (kmrcl:ensure-keyword name)
+ name)
+ (kmrcl:decode-uri-query-string val))
+ alist))
+ (cmsg-c :debug "Invalid number of #\= in ~S" name-val-list)))))))