-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
handle.lisp
126 lines (118 loc) · 4.89 KB
/
handle.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
(in-package #:org.shirakumo.radiance.core)
(defvar *debugger*)
(defun swank-connected-p ()
(let ((threads (bt:all-threads)))
(and (find-package :swank)
(find "repl-thread" threads :key #'bt:thread-name :test #'equal)
(find "reader-thread" threads :key #'bt:thread-name :test #'equal)
(find "control-thread" threads :key #'bt:thread-name :test #'equal))))
(defun maybe-invoke-debugger (condition &optional restart &rest values)
(when (case *debugger*
(:if-swank-connected (swank-connected-p))
((T) T))
(with-simple-restart (continue "Don't handle ~a." condition)
(invoke-debugger condition)))
(when restart
(when (find-restart restart)
(apply #'invoke-restart restart values))))
(defun handle-condition (condition)
(if (typep condition 'radiance-condition)
(l:debug :radiance condition)
(l:warn :radiance condition))
(l:warn :radiance "~@[(~a) ~]Handling stray condition: ~a"
(when *request* (remote *request*)) condition)
(restart-case
(maybe-invoke-debugger condition 'abort)
(abort ()
:report "Render error page."
(invoke-restart 'set-data (render-error-page condition)))))
(unless (fboundp 'render-error-page)
(defun render-error-page (condition)
(setf (return-code *response*) 500)
(setf (content-type *response*) "text/plain")
(format NIL "Internal error: ~s" condition)))
;; FIXME: Add support for STREAMs in data of response to the implementations.
;; FIXME: Access to the input stream of the body without parsing, somehow?
(define-hook request (request response))
(defun execute-request (request &optional (response (make-instance 'response)))
(declare (optimize (speed 3)))
(l:trace :core.request "Executing request: ~s ~s" request response)
(handler-bind ((error #'handle-condition))
(let ((*request* request)
(*response* response))
(restart-case
(progn
(trigger 'request request response)
(let ((result (dispatch (uri request))))
(typecase result
((or string stream function (array (unsigned-byte 8)))
(unless (data *response*)
(setf (data *response*) result)))
(pathname
(unless (data *response*)
(serve-file result)))
(response
(setf *response* result)))))
(set-data (data)
:report "Set the response data."
:interactive read-value
(if (typep data 'response)
(setf *response* data)
(setf (data *response*) data))))
(values *response* *request*))))
(defun ensure-request-hash-table (thing)
(declare (optimize (speed 3)))
(etypecase thing
(null
(make-hash-table :test 'equalp))
(hash-table
(case (hash-table-test thing)
(equalp thing)
(T (copy-hash-table thing :test 'equalp))))
(list
(let ((table (make-hash-table :test 'equalp))
(to-reverse ()))
(flet ((push-to-table (k v)
(let ((k (string k)))
(cond ((ends-with "[]" k)
(push v (gethash k table))
(pushnew k to-reverse :test #'equalp))
(T
(setf (gethash k table) v))))))
(etypecase (first thing)
((or string keyword)
(loop for (k v) on thing by #'cddr
do (push-to-table k v)))
(cons
(loop for (k . v) in thing
do (push-to-table k v)))))
(dolist (k to-reverse)
(setf (gethash k table) (nreverse (gethash k table))))
table))))
(defun request (to-uri &key (representation :internal) (http-method :GET) body-stream headers post get cookies (remote "unknown") (response (make-instance 'response)))
(declare (optimize (speed 3)))
;; KLUDGE!
;; This should be handled nicer somehow, but
;; we currently have to do it like this as we
;; would run into a problem because the domain
;; cutter route needs to set the DOMAIN on
;; *request* and has no other means to
;; communicate this information to us. Thus,
;; we first spoof the URI and *REQUEST* to
;; perform the proper routing and then switch
;; out the URIs to dispatch.
(let ((*request* (make-instance
'request
:uri to-uri
:http-method http-method
:body-stream body-stream
:headers (ensure-request-hash-table headers)
:post-data (ensure-request-hash-table post)
:get-data (ensure-request-hash-table get)
:cookies (ensure-request-hash-table cookies)
:remote remote)))
(l:trace :core.request "Received request ~a" *request*)
(setf (uri *request*) (represent-uri (uri *request*) representation))
(execute-request
*request*
response)))