reply.lisp
  1 ;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
2
3 ;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
4
5 ;;; Redistribution and use in source and binary forms, with or without
6 ;;; modification, are permitted provided that the following conditions
7 ;;; are met:
8
9 ;;; * Redistributions of source code must retain the above copyright
10 ;;; notice, this list of conditions and the following disclaimer.
11
12 ;;; * Redistributions in binary form must reproduce the above
13 ;;; copyright notice, this list of conditions and the following
14 ;;; disclaimer in the documentation and/or other materials
15 ;;; provided with the distribution.
16
17 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
18 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 (in-package :hunchentoot)
30
31 (defclass reply ()
32 ((content-type :reader content-type
33 :documentation "The outgoing 'Content-Type' http
34 header which defaults to the value of *DEFAULT-CONTENT-TYPE*.")
35 (content-length :reader content-length
36 :initform nil
37 :documentation "The outgoing 'Content-Length'
38 http header which defaults NIL. If this is NIL, Hunchentoot will
39 compute the content length.")
40 (headers-out :initform nil
41 :reader headers-out
42 :documentation "An alist of the outgoing http headers
43 not including the 'Set-Cookie', 'Content-Length', and 'Content-Type'
44 headers. Use the functions HEADER-OUT and \(SETF HEADER-OUT) to
45 modify this slot.")
46 (return-code :initform +http-ok+
47 :accessor return-code
48 :documentation "The http return code of this
49 reply. The return codes Hunchentoot can handle are defined in
50 specials.lisp.")
51 (external-format :initform *hunchentoot-default-external-format*
52 :accessor reply-external-format
53 :documentation "The external format of the reply -
54 used for character output.")
55 (cookies-out :initform nil
56 :accessor cookies-out
57 :documentation "The outgoing cookies. This slot's
58 value should only be modified by the functions defined in
59 cookies.lisp."))
60 (:documentation "Objects of this class hold all the information
61 about an outgoing reply. They are created automatically by
62 Hunchentoot and can be accessed and modified by the corresponding
63 handler.
64
65 You should not mess with the slots of these objects directly, but you
66 can subclass REPLY in order to implement your own behaviour. See the
67 REPLY-CLASS slot of the ACCEPTOR class."))
68
69 (defmethod initialize-instance :after ((reply reply) &key)
70 (setf (header-out :content-type reply) *default-content-type*))
71
72 (defun headers-out* (&optional (reply *reply*))
73 "Returns an alist of the outgoing headers associated with the
74 REPLY object REPLY."
75 (headers-out reply))
76
77 (defun cookies-out* (&optional (reply *reply*))
78 "Returns an alist of the outgoing cookies associated with the
79 REPLY object REPLY."
80 (cookies-out reply))
81
82 (defun (setf cookies-out*) (new-value &optional (reply *reply*))
83 "Sets the alist of the outgoing cookies associated with the REPLY
84 object REPLY."
85 (setf (cookies-out reply) new-value))
86
87 (defun content-type* (&optional (reply *reply*))
88 "The outgoing 'Content-Type' http header of REPLY."
89 (content-type reply))
90
91 (defun (setf content-type*) (new-value &optional (reply *reply*))
92 "Sets the outgoing 'Content-Type' http header of REPLY."
93 (setf (header-out :content-type reply) new-value))
94
95 (defun content-length* (&optional (reply *reply*))
96 "The outgoing 'Content-Length' http header of REPLY."
97 (content-length reply))
98
99 (defun (setf content-length*) (new-value &optional (reply *reply*))
100 "Sets the outgoing 'Content-Length' http header of REPLY."
101 (setf (header-out :content-length reply) new-value))
102
103 (defun return-code* (&optional (reply *reply*))
104 "The http return code of REPLY. The return codes Hunchentoot can
105 handle are defined in specials.lisp."
106 (return-code reply))
107
108 (defun (setf return-code*) (new-value &optional (reply *reply*))
109 "Sets the http return code of REPLY."
110 (setf (return-code reply) new-value))
111
112 (defun reply-external-format* (&optional (reply *reply*))
113 "The external format of REPLY which is used for character output."
114 (reply-external-format reply))
115
116 (defun (setf reply-external-format*) (new-value &optional (reply *reply*))
117 "Sets the external format of REPLY."
118 (setf (reply-external-format reply) new-value))
119
120 (defun header-out-set-p (name &optional (reply *reply*))
121 "Returns a true value if the outgoing http header named NAME has
122 been specified already. NAME should be a keyword or a string."
123 (assoc* name (headers-out reply)))
124
125 (defun header-out (name &optional (reply *reply*))
126 "Returns the current value of the outgoing http header named NAME.
127 NAME should be a keyword or a string."
128 (cdr (assoc name (headers-out reply))))
129
130 (defun cookie-out (name &optional (reply *reply*))
131 "Returns the current value of the outgoing cookie named
132 NAME. Search is case-sensitive."
133 (cdr (assoc name (cookies-out reply) :test #'string=)))
134
135 (defgeneric (setf header-out) (new-value name &optional reply)
136 (:documentation "Changes the current value of the outgoing http
137 header named NAME \(a keyword or a string). If a header with this
138 name doesn't exist, it is created.")
139 (:method (new-value (name symbol) &optional (reply *reply*))
140 ;; the default method
141 (let ((entry (assoc name (headers-out reply))))
142 (if entry
143 (setf (cdr entry) new-value)
144 (setf (slot-value reply 'headers-out)
145 (acons name new-value (headers-out reply))))
146 new-value))
147 (:method (new-value (name string) &optional (reply *reply*))
148 "If NAME is a string, it is converted to a keyword first."
149 (setf (header-out (as-keyword name :destructivep nil) reply) new-value))
150 (:method :after (new-value (name (eql :content-length)) &optional (reply *reply*))
151 "Special case for the `Content-Length' header."
152 (check-type new-value integer)
153 (setf (slot-value reply 'content-length) new-value))
154 (:method :after (new-value (name (eql :content-type)) &optional (reply *reply*))
155 "Special case for the `Content-Type' header."
156 (check-type new-value (or null string))
157 (setf (slot-value reply 'content-type) new-value)))