-
Notifications
You must be signed in to change notification settings - Fork 1
/
errors-acceptor.lisp
141 lines (126 loc) · 6.32 KB
/
errors-acceptor.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
(in-package #:hunchentoot-errors)
(defclass errors-acceptor (acceptor)
((log-request :initarg :log-request
:accessor log-requestp
:initform t
:documentation "When enabled, request information is written to the log.")
(debug-request :initarg :debug-request
:accessor debug-requestp
:initform t
:documentation "When enabled, request information is printed in Hunchentoot status error pages.")
(log-session :initarg :log-session
:accessor log-sessionp
:initform t
:documentation "When enabled, session information is written to the log.")
(debug-session :initarg :debug-session
:accessor debug-sessionp
:initform t
:documentation "When enabled, session information is printed in Hunchentoot status error pages."))
(:documentation "Subclasses of this acceptor augment Hunchentoot error pages and logs with request and session information."))
(defgeneric print-request (request format stream)
(:documentation "Prints REQUEST to STREAM in FORMAT"))
(defmethod print-request ((request request) format stream)
(prin1 request stream))
(defgeneric print-session (session format stream)
(:documentation "Prints SESSION to STREAM in FORMAT"))
(defmethod print-session ((session session) format stream)
(prin1 session stream))
(defmethod print-session ((session session) (format (eql :text-log)) stream)
(loop for (key . value) in (hunchentoot::session-data session)
do (format stream " ~s: ~s~%" key value)))
(defmethod print-request ((request request) (format (eql :text-log)) stream)
(format stream " uri: ~a~%" (request-uri request))
(format stream " method: ~a~%" (request-method request))
(format stream " headers:~%")
(loop for (key . value) in (hunchentoot:headers-in request)
do (format stream " ~a: ~a~%" key value))
(when (member (request-method request) '(:patch :post))
(format stream " post parameters:~%")
(loop for (key . value) in (hunchentoot:post-parameters request)
do (format stream " ~a: ~a~%" key value))))
(defmethod print-request ((request request) (format (eql :html)) stream)
(format stream "<div class=\"request\">")
(format stream "<h1>Request</h1>~%")
(format stream "<p><b>Uri:</b> ~a</p>~%" (request-uri request))
(format stream "<p><b>Method:</b> ~a</p>~%" (request-method request))
(format stream "<p><b>Headers:</b>~%<ul>")
(loop for (key . value) in (hunchentoot:headers-in request)
do (format stream "<li><i>~a:</i> ~a</li>~%" key value))
(format stream "</ul></p>")
(when (member (request-method request) '(:patch :post))
(format stream "<p><b>Post parameters:</b>~%<ul>")
(loop for (key . value) in (hunchentoot:post-parameters request)
do (format stream "<li><i>~a:</i> ~a</li>~%" key value))
(format stream "</ul></p>"))
(format stream "</div>"))
(defmethod print-session ((session session) (format (eql :html)) stream)
(format stream "<div class=\"session\">")
(format stream "<h1>Session</h1>~%<ul>")
(loop for (key . value) in (hunchentoot::session-data session)
do (format stream "<li>~s: <code>~s</code>~%</li>"
key (hunchentoot:escape-for-html (princ-to-string value))))
(format stream "</ul></div>"))
(defun accept-format (&optional (content-type (hunchentoot:header-in "accept" *request*)))
(or (and content-type
(let ((accepts (mimeparse:best-match
(list "text/lisp"
"application/lisp"
"text/xml"
"application/xml"
"text/html"
"application/json")
content-type)))
(string-case:string-case (accepts :default :text)
("text/xml" :xml)
("application/xml" :xml)
("text/html" :html)
("application/json" :json)
("text/lisp" :sexp)
("application/lisp" :sexp))))
:text))
(defgeneric acceptor-log-error (stream acceptor log-level format-string &rest format-arguments))
(defmethod acceptor-log-error (stream (acceptor errors-acceptor) log-level format-string &rest format-arguments)
;; This snippet is from original Hunchentoot:
(format stream "[~A~@[ [~A]~]] ~?~%"
(hunchentoot::iso-time) log-level
format-string format-arguments)
;; This part is hunchentoot-errors specific:
(when (and (log-requestp acceptor)
(boundp '*request*))
(format stream "HTTP REQUEST:~%")
(print-request *request* :text-log stream))
(when (and (log-sessionp acceptor)
(boundp '*session*)
(not (null *session*)))
(format stream "~%SESSION:~%")
(print-session *session* :text-log stream))
(terpri stream))
(defmethod acceptor-log-message ((acceptor errors-acceptor) log-level format-string &rest format-arguments)
"Sends a formatted message to the destination denoted by (ACCEPTOR-MESSAGE-LOG-DESTINATION ACCEPTOR).
FORMAT and ARGS are as in FORMAT.
LOG-LEVEL is a keyword denoting the log level or NIL in which case it is ignored."
(if (not (eq log-level :error))
(call-next-method)
;; else
(hunchentoot::with-log-stream (stream (acceptor-message-log-destination acceptor) hunchentoot::*message-log-lock*)
(handler-case
(apply #'acceptor-log-error stream acceptor log-level format-string format-arguments)
(error (e)
(ignore-errors
(format *trace-output* "error ~A while writing to error log, error not logged~%" e)))))))
(defmethod acceptor-status-message ((acceptor errors-acceptor) http-status-code &key &allow-other-keys)
(if (not *show-lisp-errors-p*)
(call-next-method)
(concatenate
'string
(call-next-method)
(with-output-to-string (msg)
(let ((format (accept-format)))
(when (and (debug-requestp acceptor)
(boundp '*request*)
(not (null *request*)))
(print-request *request* format msg))
(when (and (debug-sessionp acceptor)
(boundp '*session*)
(not (null *session*)))
(print-session *session* format msg)))))))