-
-
Notifications
You must be signed in to change notification settings - Fork 87
/
Copy pathclack.lisp
110 lines (105 loc) · 3.85 KB
/
clack.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
(defpackage clack
(:use :cl)
(:import-from :clack.handler
:run
:stop)
(:import-from :clack.util
:find-handler)
(:import-from :lack
:builder)
(:import-from :alexandria
:delete-from-plist)
(:export :clackup
:eval-file
:stop))
(in-package :clack)
(defvar *app-file-cache*
(make-hash-table :test 'equal))
(defun %load-file (file)
(with-open-file (in file)
(let ((*package* *package*)
(*readtable* *readtable*)
(*load-pathname* file)
(*load-truename* file))
(loop with results
with eof = '#:eof
for form = (read in nil eof)
until (eq form eof)
do (setf results (multiple-value-list (eval form)))
finally
(return (apply #'values results))))))
(defun eval-file (file)
"Safer way to read and eval a file content. This function returns the last value."
(setf file (probe-file file))
(check-type file pathname)
(let ((modified-at (file-write-date file)))
(cond
((< (car (gethash file *app-file-cache* '(0 . nil)))
modified-at)
(let ((app (%load-file file)))
(setf (gethash file *app-file-cache*)
(cons modified-at app))
app))
(t
(cdr (gethash file *app-file-cache*))))))
(defmacro with-handle-interrupt (int-handler &body body)
(let ((main (gensym "MAIN")))
`(flet ((,main () ,@body))
#+(or sbcl ccl clisp allegro ecl)
(handler-case
(let (#+ccl (ccl:*break-hook* (lambda (condition hook)
(declare (ignore hook))
(error condition))))
(,main))
(#+sbcl sb-sys:interactive-interrupt
#+ccl ccl:interrupt-signal-condition
#+clisp system::simple-interrupt-condition
#+ecl ext:interactive-interrupt
#+allegro excl:interrupt-signal
()
(funcall ,int-handler)))
#-(or sbcl ccl clisp allegro ecl)
(,main))))
(defun clackup (app &rest args
&key (server :hunchentoot)
(address "127.0.0.1")
(port 5000)
swank-interface
swank-port
(debug t)
silent
(use-thread #+thread-support t #-thread-support nil)
(use-default-middlewares t)
&allow-other-keys)
(declare (ignore swank-interface swank-port))
#-thread-support
(when use-thread
(error ":use-thread is T though there's no thread support."))
(flet ((buildapp (app)
(let* ((*features* (cons :clackup *features*))
(app (typecase app
((or pathname string)
(eval-file app))
(otherwise app))))
(builder
(if use-default-middlewares
:backtrace
nil)
app))))
(let ((app (buildapp app)))
;; Ensure the handler to be loaded.
(find-handler server)
(when (and (not use-thread)
(not silent))
(format t "~&~:(~A~) server is going to start.~%Listening on ~A:~A.~%" server address port))
(with-handle-interrupt (lambda ()
(format *error-output* "Interrupted"))
(prog1
(apply #'clack.handler:run app server
:port port
:debug debug
:use-thread use-thread
(delete-from-plist args :server :port :debug :silent :use-thread))
(when (and use-thread
(not silent))
(format t "~&~:(~A~) server is started.~%Listening on ~A:~A.~%" server address port)))))))