root/trunk/thirdparty/cl+ssl/test.lisp

Revision 2698, 10.9 kB (checked in by hans, 10 months ago)

updated cl+ssl from CVS trunk - now with deadlines

Line 
1 ;;; Copyright (C) 2008  David Lichteblau
2 ;;; See LICENSE for details.
3
4 #|
5 (load "test.lisp")
6 |#
7
8 (defpackage :ssl-test
9   (:use :cl))
10 (in-package :ssl-test)
11
12 (defvar *port* 8080)
13 (defvar *cert* "/home/david/newcert.pem")
14 (defvar *key* "/home/david/newkey.pem")
15
16 (eval-when (:compile-toplevel :load-toplevel :execute)
17   (asdf:operate 'asdf:load-op :trivial-sockets)
18   (asdf:operate 'asdf:load-op :bordeaux-threads))
19
20 (defparameter *tests* '())
21
22 (defvar *sockets* '())
23 (defvar *sockets-lock* (bordeaux-threads:make-lock))
24
25 (defun record-socket (socket)
26   (unless (integerp socket)
27     (bordeaux-threads:with-lock-held (*sockets-lock*)
28       (push socket *sockets*)))
29   socket)
30
31 (defun close-socket (socket &key abort)
32   (if (streamp socket)
33       (close socket :abort abort)
34       (trivial-sockets:close-server socket)))
35
36 (defun check-sockets ()
37   (let ((failures nil))
38     (bordeaux-threads:with-lock-held (*sockets-lock*)
39       (dolist (socket *sockets*)
40         (when (close-socket socket :abort t)
41           (push socket failures)))
42       (setf *sockets* nil))
43     #-sbcl                              ;fixme
44     (when failures
45       (error "failed to close sockets properly:~{  ~A~%~}" failures))))
46
47 (defmacro deftest (name &body body)
48   `(progn
49      (defun ,name ()
50        (format t "~%----- ~A ----------------------------~%" ',name)
51        (handler-case
52            (progn
53              ,@body
54              (check-sockets)
55              (format t "===== [OK] ~A ====================~%" ',name)
56              t)
57          (error (c)
58            (when (typep c 'trivial-sockets:socket-error)
59              (setf c (trivial-sockets:socket-nested-error c)))
60            (format t "~%===== [FAIL] ~A: ~A~%" ',name c)
61            (handler-case
62                (check-sockets)
63              (error (c)
64                (format t "muffling follow-up error ~A~%" c)))
65            nil)))
66      (push ',name *tests*)))
67
68 (defun run-all-tests ()
69   (unless (probe-file *cert*) (error "~A not found" *cert*))
70   (unless (probe-file *key*) (error "~A not found" *key*))
71   (let ((n 0)
72         (nok 0))
73     (dolist (test (reverse *tests*))
74       (when (funcall test)
75         (incf nok))
76       (incf n))
77     (format t "~&passed ~D/~D tests~%" nok n)))
78
79 (define-condition quit (condition)
80   ())
81
82 (defparameter *please-quit* t)
83
84 (defun make-test-thread (name init main &rest args)
85   "Start a thread named NAME, wait until it has funcalled INIT with ARGS
86    as arguments, then continue while the thread concurrently funcalls MAIN
87    with INIT's return values as arguments."
88   (let ((cv (bordeaux-threads:make-condition-variable))
89         (lock (bordeaux-threads:make-lock name))
90         ;; redirect io manually, because swan's global redirection isn't as
91         ;; global as one might hope
92         (out *terminal-io*)
93         (init-ok nil))
94     (bordeaux-threads:with-lock-held (lock)
95       (setf *please-quit* nil)
96       (prog1
97           (bordeaux-threads:make-thread
98            (lambda ()
99              (flet ((notify ()
100                       (bordeaux-threads:with-lock-held (lock)
101                         (bordeaux-threads:condition-notify cv))))
102                (let ((*terminal-io* out)
103                      (*standard-output* out)
104                      (*trace-output* out)
105                      (*error-output* out))
106                  (handler-case
107                      (let ((values (multiple-value-list (apply init args))))
108                        (setf init-ok t)
109                        (notify)
110                        (apply main values))
111                    (quit ()
112                      (notify)
113                      t)
114                    (error (c)
115                      (when (typep c 'trivial-sockets:socket-error)
116                        (setf c (trivial-sockets:socket-nested-error c)))
117                      (format t "aborting test thread ~A: ~A" name c)
118                      (notify)
119                      nil)))))
120            :name name)
121         (bordeaux-threads:condition-wait cv lock)
122         (unless init-ok
123           (error "failed to start background thread"))))))
124
125 (defmacro with-thread ((name init main &rest args) &body body)
126   `(invoke-with-thread (lambda () ,@body)
127                        ,name
128                        ,init
129                        ,main
130                        ,@args))
131
132 (defun invoke-with-thread (body name init main &rest args)
133   (let ((thread (apply #'make-test-thread name init main args)))
134     (unwind-protect
135          (funcall body)
136       (setf *please-quit* t)
137       (loop
138          for delay = 0.0001 then (* delay 2)
139          while (and (< delay 0.5) (bordeaux-threads:thread-alive-p thread))
140          do
141            (sleep delay))
142       (when (bordeaux-threads:thread-alive-p thread)
143         (format t "~&thread doesn't want to quit, killing it~%")
144         (force-output)
145         (bordeaux-threads:interrupt-thread thread (lambda () (error 'quit)))
146         (loop
147            for delay = 0.0001 then (* delay 2)
148            while (bordeaux-threads:thread-alive-p thread)
149            do
150            (sleep delay))))))
151
152 (defun init-server (&key (unwrap-stream-p t))
153   (format t "~&SSL server listening on port ~d~%" *port*)
154   (values (record-socket (trivial-sockets:open-server :port *port*))
155           unwrap-stream-p))
156
157 (defun test-server (listening-socket unwrap-stream-p)
158   (format t "~&SSL server accepting...~%")
159   (unwind-protect
160        (let* ((socket (record-socket
161                        (trivial-sockets:accept-connection
162                         listening-socket
163                         :element-type '(unsigned-byte 8))))
164               (callback nil))
165          (when (eq unwrap-stream-p :caller)
166            (setf callback (let ((s socket)) (lambda () (close-socket s))))
167            (setf socket (cl+ssl:stream-fd socket))
168            (setf unwrap-stream-p nil))
169          (let ((client (record-socket
170                         (cl+ssl:make-ssl-server-stream
171                          socket
172                          :unwrap-stream-p unwrap-stream-p
173                          :close-callback callback
174                          :external-format :iso-8859-1
175                          :certificate *cert*
176                          :key *key*))))
177            (unwind-protect
178                 (loop
179                    for line = (prog2
180                                   (when *please-quit* (return))
181                                   (read-line client nil)
182                                 (when *please-quit* (return)))
183                    while line
184                    do
185                      (cond
186                        ((equal line "freeze")
187                         (format t "~&Freezing on client request~%")
188                         (loop
189                            (sleep 1)
190                            (when *please-quit* (return))))
191                        (t
192                         (format t "~&Responding to query ~A...~%" line)
193                         (format client "(echo ~A)~%" line)
194                         (force-output client))))
195              (close-socket client))))
196     (close-socket listening-socket)))
197
198 (defun init-client (&key (unwrap-stream-p t))
199   (let ((socket (record-socket
200                  (trivial-sockets:open-stream
201                   "127.0.0.1"
202                   *port*
203                   :element-type '(unsigned-byte 8))))
204         (callback nil))
205     (when (eq unwrap-stream-p :caller)
206       (setf callback (let ((s socket)) (lambda () (close-socket s))))
207       (setf socket (cl+ssl:stream-fd socket))
208       (setf unwrap-stream-p nil))
209     (cl+ssl:make-ssl-client-stream
210      socket
211      :unwrap-stream-p unwrap-stream-p
212      :close-callback callback
213      :external-format :iso-8859-1)))
214
215 ;;; Simple echo-server test.  Write a line and check that the result
216 ;;; watches, three times in a row.
217 (deftest echo
218   (with-thread ("simple server" #'init-server #'test-server)
219     (with-open-stream (socket (init-client))
220       (write-line "test" socket)
221       (force-output socket)
222       (assert (equal (read-line socket) "(echo test)"))
223       (write-line "test2" socket)
224       (force-output socket)
225       (assert (equal (read-line socket) "(echo test2)"))
226       (write-line "test3" socket)
227       (force-output socket)
228       (assert (equal (read-line socket) "(echo test3)")))))
229
230 ;;; Run tests with different BIO setup strategies:
231 ;;;   - :UNWRAP-STREAMS T
232 ;;;     In this case, CL+SSL will convert the socket to a file descriptor.
233 ;;;   - :UNWRAP-STREAMS :CLIENT
234 ;;;     Convert the socket to a file descriptor manually, and give that
235 ;;;     to CL+SSL.
236 ;;;   - :UNWRAP-STREAMS NIL
237 ;;;     Let CL+SSL write to the stream directly, using the Lisp BIO.
238 (macrolet ((deftests (name (var &rest values) &body body)
239              `(progn
240                 ,@(loop
241                      for value in values
242                      collect
243                      `(deftest ,(intern (format nil "~A-~A" name value))
244                         (let ((,var ',value))
245                           ,@body))))))
246
247   (deftests unwrap-strategy (usp nil t :caller)
248     (with-thread ("echo server for strategy test"
249                   (lambda () (init-server :unwrap-stream-p usp))
250                   #'test-server)
251       (with-open-stream (socket (init-client :unwrap-stream-p usp))
252         (write-line "test" socket)
253         (force-output socket)
254         (assert (equal (read-line socket) "(echo test)")))))
255
256   #+clozure-common-lisp
257   (deftests read-deadline (usp nil t)
258     (with-thread ("echo server for deadline test"
259                   (lambda () (init-server :unwrap-stream-p usp))
260                   #'test-server)
261       (let* ((deadline
262               (+ (get-internal-real-time)
263                  (* 3 internal-time-units-per-second)))
264              (low
265               (record-socket
266                (ccl:make-socket
267                 :address-family :internet
268                 :connect :active
269                 :type :stream
270                 :remote-host "127.0.0.1"
271                 :remote-port *port*
272                 :deadline deadline))))
273         (with-open-stream
274             (socket
275              (cl+ssl:make-ssl-client-stream
276               low
277               :unwrap-stream-p usp
278               :external-format :iso-8859-1))
279           (write-line "test" socket)
280           (force-output socket)
281           (assert (equal (read-line socket) "(echo test)"))
282           (handler-case
283               (progn
284                 (read-char socket)
285                 (error "unexpected data"))
286             (ccl::communication-deadline-expired ()))))))
287
288   #+sbcl
289   (deftests read-deadline (usp nil t :caller)
290     (with-thread ("echo server for deadline test"
291                   (lambda () (init-server :unwrap-stream-p usp))
292                   #'test-server)
293       (sb-sys:with-deadline (:seconds 3)
294         (with-open-stream (socket (init-client :unwrap-stream-p usp))
295           (write-line "test" socket)
296           (force-output socket)
297           (assert (equal (read-line socket) "(echo test)"))
298           (handler-case
299               (progn
300                 (read-char socket)
301                 (error "unexpected data"))
302             (sb-sys:deadline-timeout ()))))))
303
304   #+clozure-common-lisp
305   (deftests write-deadline (usp nil t)
306     (with-thread ("echo server for deadline test"
307                   (lambda () (init-server :unwrap-stream-p usp))
308                   #'test-server)
309       (let* ((deadline
310               (+ (get-internal-real-time)
311                  (* 3 internal-time-units-per-second)))
312              (low
313               (record-socket
314                (ccl:make-socket
315                 :address-family :internet
316                 :connect :active
317                 :type :stream
318                 :remote-host "127.0.0.1"
319                 :remote-port *port*
320                 :deadline deadline)))
321              (socket
322               (cl+ssl:make-ssl-client-stream
323                low
324                :unwrap-stream-p usp
325                :external-format :iso-8859-1)))
326         (unwind-protect
327             (progn
328               (write-line "test" socket)
329               (force-output socket)
330               (assert (equal (read-line socket) "(echo test)"))
331               (write-line "freeze" socket)
332               (force-output socket)
333               (let ((n 0))
334                 (handler-case
335                     (loop
336                        (write-line "deadbeef" socket)
337                        (incf n))
338                   (ccl::communication-deadline-expired ()))
339                 ;; should have written a couple of lines before the deadline:
340                 (assert (> n 100))))
341           (handler-case
342               (close-socket socket :abort t)
343             (ccl::communication-deadline-expired ()))))))
344
345   #+sbcl
346   (deftests write-deadline (usp nil t)
347     (with-thread ("echo server for deadline test"
348                   (lambda () (init-server :unwrap-stream-p usp))
349                   #'test-server)
350       (with-open-stream (socket (init-client :unwrap-stream-p usp))
351         (unwind-protect
352              (sb-sys:with-deadline (:seconds 3)
353                (write-line "test" socket)
354                (force-output socket)
355                (assert (equal (read-line socket) "(echo test)"))
356                (write-line "freeze" socket)
357                (force-output socket)
358                (let ((n 0))
359                  (handler-case
360                      (loop
361                         (write-line "deadbeef" socket)
362                         (incf n))
363                    (sb-sys:deadline-timeout ()))
364                  ;; should have written a couple of lines before the deadline:
365                  (assert (> n 100))))
366           (handler-case
367               (close-socket socket :abort t)
368             (sb-sys:deadline-timeout ())))))))
369
370 #+(or)
371 (run-all-tests)
Note: See TracBrowser for help on using the browser.