1 ;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
2 ;;; $Header: /usr/local/cvsrep/hunchentoot/test/test.lisp,v 1.24 2008/03/06 07:46:53 edi Exp $
4 ;;; Copyright (c) 2004-2009, Dr. Edmund Weitz. All rights reserved.
6 ;;; Redistribution and use in source and binary forms, with or without
7 ;;; modification, are permitted provided that the following conditions
10 ;;; * Redistributions of source code must retain the above copyright
11 ;;; notice, this list of conditions and the following disclaimer.
13 ;;; * Redistributions in binary form must reproduce the above
14 ;;; copyright notice, this list of conditions and the following
15 ;;; disclaimer in the documentation and/or other materials
16 ;;; provided with the distribution.
18 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
19 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 (in-package :hunchentoot-test
)
32 (defvar *this-file
* (load-time-value
33 (or #.
*compile-file-pathname
* *load-pathname
*)))
35 (defmacro with-html
(&body body
)
36 `(with-html-output-to-string (*standard-output
* nil
:prologue t
)
39 (defun hunchentoot-link ()
40 (with-html-output (*standard-output
*)
41 (:a
:href
"http://weitz.de/hunchentoot/" "Hunchentoot")))
44 (with-html-output (*standard-output
*)
46 (:a
:href
"/hunchentoot/test" "Back to menu")))))
48 (defmacro with-lisp-output
((var) &body body
)
49 `(let ((*package
* (find-package :hunchentoot-test-user
)))
50 (with-output-to-string (,var
#+:lispworks nil
51 #+:lispworks
:element-type
52 #+:lispworks
'lw
:simple-char
)
55 (defmacro info-table
(&rest forms
)
56 (let ((=value
= (gensym))
58 `(with-html-output (*standard-output
*)
59 (:p
(:table
:border
1 :cellpadding
2 :cellspacing
0
63 " provides about this request:"))
64 ,@(loop for form in forms
65 collect
`(:tr
(:td
:valign
"top"
66 (:pre
:style
"padding: 0px"
67 (esc (with-lisp-output (s) (pprint ',form s
)))))
69 (:pre
:style
"padding: 0px"
70 (esc (with-lisp-output (s)
71 (loop for
,=value
= in
(multiple-value-list ,form
)
72 for
,=first
= = t then nil
75 do
(pprint ,=value
= s
))))))))))
78 (defun authorization-page ()
79 (multiple-value-bind (user password
)
81 (cond ((and (equal user
"nanook")
82 (equal password
"igloo"))
85 (:head
(:title
"Hunchentoot page with Basic Authentication"))
87 (:h2
(hunchentoot-link)
88 " page with Basic Authentication")
89 (info-table (header-in* :authorization
)
92 (require-authorization)))))
94 (defparameter *test-image
*
96 (with-open-file (in (make-pathname :name
"fz" :type
"jpg" :version nil
97 :defaults
*this-file
*)
98 :element-type
'flex
:octet
)
99 (let ((image-data (make-array (file-length in
)
100 :element-type
'flex
:octet
)))
101 (read-sequence image-data in
)
104 (defun image-ram-page ()
105 (setf (content-type*) "image/jpeg")
112 (:head
(:title
"Hunchentoot Information"))
114 (:h2
(hunchentoot-link) " Information Page")
115 (:p
"This page has been called "
117 (fmt "~[~;once~;twice~:;~:*~R times~]" (incf count
)))
118 " since its handler was compiled.")
120 (acceptor-address *acceptor
*)
121 (acceptor-port *acceptor
*)
134 (server-protocol*)))))))
138 (log-message :error
"Oops \(error log level).")
139 (log-message :warning
"Oops \(warning log level).")
140 (log-message :info
"Oops \(info log level).")
141 (error "Errors were triggered on purpose. Check your error log.")
143 (:body
"You should never see this sentence..."))))
146 (redirect "/hunchentoot/test/info.html?redirected=1"))
149 (setf (return-code*) +http-forbidden
+)
152 (defun cookie-test ()
153 (set-cookie "pumpkin" :value
"barking")
157 (:head
(:title
"Hunchentoot cookie test"))
159 (:h2
(hunchentoot-link)
161 (:p
"You might have to reload this page to see the cookie value.")
162 (info-table (cookie-in "pumpkin")
163 (mapcar 'car
(cookies-in*)))))))
165 (defun session-test ()
166 (let ((new-foo-value (post-parameter "new-foo-value")))
168 (setf (session-value 'foo
) new-foo-value
)))
169 (let ((new-bar-value (post-parameter "new-bar-value")))
171 (setf (session-value 'bar
) new-bar-value
)))
175 (:head
(:title
"Hunchentoot session test"))
177 (:h2
(hunchentoot-link)
179 (:p
"Use the forms below to set new values for "
183 ". You can later return to this page to check if
184 they're still set. Also, try to use another browser at the same
185 time or try with cookies disabled.")
186 (:p
(:form
:method
:post
191 :name
"new-foo-value"
192 :value
(or (session-value 'foo
) ""))))
193 (:p
(:form
:method
:post
198 :name
"new-bar-value"
199 :value
(or (session-value 'bar
) ""))))
200 (info-table (session-cookie-name *acceptor
*)
201 (cookie-in (session-cookie-name *acceptor
*))
202 (mapcar 'car
(cookies-in*))
204 (session-value 'bar
))))))
206 (defun parameter-test (&key
(method :get
) (charset :iso-8859-1
))
208 (recompute-request-parameters :external-format
209 (flex:make-external-format charset
:eol-style
:lf
))
210 (setf (content-type*)
211 (format nil
"text/html; charset=~A" charset
))
214 (:head
(:title
(fmt "Hunchentoot ~A parameter test" method
)))
216 (:h2
(hunchentoot-link)
217 (fmt " ~A parameter test with charset ~A" method charset
))
218 (:p
"Enter some non-ASCII characters in the input field below
219 and see what's happening.")
226 (:get
(info-table (query-string*)
227 (map 'list
'char-code
(get-parameter "foo"))
228 (get-parameter "foo")))
229 (:post
(info-table (raw-post-data)
230 (map 'list
'char-code
(post-parameter "foo"))
231 (post-parameter "foo"))))))))
233 (defun parameter-test-latin1-get ()
234 (parameter-test :method
:get
:charset
:iso-8859-1
))
236 (defun parameter-test-latin1-post ()
237 (parameter-test :method
:post
:charset
:iso-8859-1
))
239 (defun parameter-test-utf8-get ()
240 (parameter-test :method
:get
:charset
:utf-8
))
242 (defun parameter-test-utf8-post ()
243 (parameter-test :method
:post
:charset
:utf-8
))
245 ;; this should not be the same directory as *TMP-DIRECTORY* and it
246 ;; should be initially empty (or non-existent)
247 (defvar *tmp-test-directory
*
248 #+(or :win32
:mswindows
) #p
"c:\\hunchentoot-temp\\test\\"
249 #-
(or :win32
:mswindows
) #p
"/tmp/hunchentoot/test/")
251 (defvar *tmp-test-files
* nil
)
254 (defun handle-file (post-parameter)
255 (when (and post-parameter
256 (listp post-parameter
))
257 (destructuring-bind (path file-name content-type
)
259 (let ((new-path (make-pathname :name
(format nil
"hunchentoot-test-~A"
262 :defaults
*tmp-test-directory
*)))
263 ;; strip directory info sent by Windows browsers
264 (when (search "Windows" (user-agent) :test
'char-equal
)
265 (setq file-name
(cl-ppcre:regex-replace
".*\\\\" file-name
"")))
266 (rename-file path
(ensure-directories-exist new-path
))
267 (push (list new-path file-name content-type
) *tmp-test-files
*))))))
269 (defun clean-tmp-dir ()
270 (loop for
(path . nil
) in
*tmp-test-files
*
271 when
(probe-file path
)
272 do
(ignore-errors (delete-file path
)))
273 (setq *tmp-test-files
* nil
))
275 (defun upload-test ()
276 (let (post-parameter-p)
277 (when (post-parameter "file1")
278 (handle-file (post-parameter "file1"))
279 (setq post-parameter-p t
))
280 (when (post-parameter "file2")
281 (handle-file (post-parameter "file2"))
282 (setq post-parameter-p t
))
283 (when (post-parameter "clean")
285 (setq post-parameter-p t
))
286 (when post-parameter-p
287 ;; redirect so user can safely use 'Back' button
288 (redirect (script-name*))))
292 (:head
(:title
"Hunchentoot file upload test"))
294 (:h2
(hunchentoot-link)
296 (:form
:method
:post
:enctype
"multipart/form-data"
303 (:p
(:input
:type
:submit
)))
304 (when *tmp-test-files
*
307 (:table
:border
1 :cellpadding
2 :cellspacing
0
308 (:tr
(:td
:colspan
3 (:b
"Uploaded files")))
309 (loop for
(path file-name nil
) in
*tmp-test-files
*
312 (:tr
(:td
:align
"right" (str counter
))
313 (:td
(:a
:href
(format nil
"files/~A?path=~A"
314 (url-encode file-name
)
315 (url-encode (namestring path
)))
319 (with-open-file (in path
)
323 (:p
(:input
:type
:submit
:name
"clean" :value
"Delete uploaded files")))))
327 (let* ((path (get-parameter "path"))
329 (find (pathname path
) *tmp-test-files
*
330 :key
'first
:test
'equal
))))
332 (setf (return-code*) +http-not-found
+)
333 (return-from send-file
))
334 (handle-static-file path
(third file-info
))))
336 (defparameter *headline
*
338 (format nil
"Hunchentoot test menu (see file <code>~A</code>)"
339 (truename (merge-pathnames (make-pathname :type
"lisp") *this-file
*)))))
341 (defvar *utf-8
* (flex:make-external-format
:utf-8
:eol-style
:lf
))
343 (defvar *utf-8-file
* (merge-pathnames "UTF-8-demo.html" *this-file
*)
344 "Demo file stolen from <http://www.w3.org/2001/06/utf-8-test/>.")
346 (defun stream-direct ()
347 (setf (content-type*) "text/html; charset=utf-8")
348 (let ((stream (send-headers))
349 (buffer (make-array 1024 :element-type
'flex
:octet
)))
350 (with-open-file (in *utf-8-file
* :element-type
'flex
:octet
)
351 (loop for pos
= (read-sequence buffer in
)
353 do
(write-sequence buffer stream
:end pos
)))))
355 (defun stream-direct-utf-8 ()
356 (setf (content-type*) "text/html; charset=utf-8")
357 (let ((stream (flex:make-flexi-stream
(send-headers) :external-format
*utf-8
*)))
358 (with-open-file (in (merge-pathnames "UTF-8-demo.html" *this-file
*)
359 :element-type
'flex
:octet
)
360 (setq in
(flex:make-flexi-stream in
:external-format
*utf-8
*))
361 (loop for line
= (read-line in nil nil
)
363 do
(write-line line stream
)))))
365 (defun stream-direct-utf-8-string ()
366 (setf (content-type*) "text/html; charset=utf-8"
367 (reply-external-format*) *utf-8
*)
368 (with-open-file (in (merge-pathnames "UTF-8-demo.html" *this-file
*)
369 :element-type
'flex
:octet
)
370 (let ((string (make-array (file-length in
)
371 :element-type
#-
:lispworks
'character
#+:lispworks
'lw
:simple-char
373 (setf in
(flex:make-flexi-stream in
:external-format
*utf-8
*)
374 (fill-pointer string
) (read-sequence string in
))
377 (define-easy-handler (easy-demo :uri
"/hunchentoot/test/easy-demo.html"
378 :default-request-type
:post
)
379 (first-name last-name
380 (age :parameter-type
'integer
)
381 (implementation :parameter-type
'keyword
)
382 (meal :parameter-type
'(hash-table boolean
))
383 (team :parameter-type
'list
))
386 (:head
(:title
"Hunchentoot \"easy\" handler example"))
388 (:h2
(hunchentoot-link)
389 " \"Easy\" handler example")
390 (:p
(:form
:method
:post
391 (:table
:border
1 :cellpadding
2 :cellspacing
0
394 (:td
(:input
:type
:text
396 :value
(or first-name
"Donald"))))
399 (:td
(:input
:type
:text
401 :value
(or last-name
"Duck"))))
404 (:td
(:input
:type
:text
406 :value
(or age
42))))
408 (:td
"Implementation:")
409 (:td
(:select
:name
"implementation"
410 (loop for
(value option
) in
'((:lispworks
"LispWorks")
411 (:allegro
"AllegroCL")
414 (:openmcl
"OpenMCL"))
416 (:option
:value value
417 :selected
(eq value implementation
)
420 (:td
:valign
:top
"Meal:")
421 (:td
(loop for choice in
'("Burnt weeny sandwich"
425 "Twenty small cigars"
428 (:input
:type
"checkbox"
429 :name
(format nil
"meal{~A}" choice
)
430 :checked
(gethash choice meal
)
434 (:td
:valign
:top
"Team:")
435 (:td
(loop for player in
'("Beckenbauer"
438 ;; without accent (for SBCL)
442 (:input
:type
"checkbox"
445 :checked
(member player team
:test
'string
=)
450 (:input
:type
"submit"))))))
451 (info-table first-name
455 (loop :for choice
:being
:the
:hash-keys
:of meal
:collect choice
)
456 (gethash "Yellow snow" meal
)
464 (:link
:rel
"shortcut icon"
465 :href
"/hunchentoot/test/favicon.ico" :type
"image/x-icon")
466 (:title
"Hunchentoot test menu"))
468 (:h2
(str *headline
*))
469 (:table
:border
0 :cellspacing
4 :cellpadding
4
470 (:tr
(:td
(:a
:href
"/hunchentoot/test/info.html?foo=bar"
471 "Info provided by Hunchentoot")))
472 (:tr
(:td
(:a
:href
"/hunchentoot/test/cookie.html"
474 (:tr
(:td
(:a
:href
"/hunchentoot/test/session.html"
476 (:tr
(:td
(:a
:href
"/hunchentoot/test/parameter_latin1_get.html"
477 "GET parameter handling with LATIN-1 charset")))
478 (:tr
(:td
(:a
:href
"/hunchentoot/test/parameter_latin1_post.html"
479 "POST parameter handling with LATIN-1 charset")))
480 (:tr
(:td
(:a
:href
"/hunchentoot/test/parameter_utf8_get.html"
481 "GET parameter handling with UTF-8 charset")))
482 (:tr
(:td
(:a
:href
"/hunchentoot/test/parameter_utf8_post.html"
483 "POST parameter handling with UTF-8 charset")))
484 (:tr
(:td
(:a
:href
"/hunchentoot/test/redir.html"
485 "Redirect \(302) to info page above")))
486 (:tr
(:td
(:a
:href
"/hunchentoot/test/authorization.html"
488 " (user 'nanook', password 'igloo')"))
489 (:tr
(:td
(:a
:href
"/hunchentoot/code/test-handlers.lisp"
490 "The source code of this test")))
491 (:tr
(:td
(:a
:href
"/hunchentoot/test/image.jpg"
492 "Binary data, delivered from file")
494 (:tr
(:td
(:a
:href
"/hunchentoot/test/image-ram.jpg"
495 "Binary data, delivered from RAM")
497 (:tr
(:td
(:a
:href
"/hunchentoot/test/easy-demo.html"
498 "\"Easy\" handler example")))
499 (:tr
(:td
(:a
:href
"/hunchentoot/test/utf8-binary.txt"
501 " \(writing octets directly to the stream)"))
502 (:tr
(:td
(:a
:href
"/hunchentoot/test/utf8-character.txt"
504 " \(writing UTF-8 characters directly to the stream)"))
505 (:tr
(:td
(:a
:href
"/hunchentoot/test/utf8-string.txt"
507 " \(returning a string)"))
508 (:tr
(:td
(:a
:href
"/hunchentoot/test/upload.html"
510 (:tr
(:td
(:a
:href
"/hunchentoot/test/forbidden.html"
511 "Forbidden \(403) page")))
512 (:tr
(:td
(:a
:href
"/hunchentoot/test/oops.html"
514 " \(output depends on "
515 (:a
:href
"http://weitz.de/hunchentoot/#*show-lisp-errors-p*"
516 (:code
"*SHOW-LISP-ERRORS-P*"))
517 (fmt " \(currently ~S))" *show-lisp-errors-p
*)))
518 (:tr
(:td
(:a
:href
"/hunchentoot/foo"
521 (:a
:href
"http://weitz.de/hunchentoot/#*default-handler*"
522 (:code
"*DEFAULT-HANDLER*")))))))))
524 (setq *dispatch-table
*
526 (list 'dispatch-easy-handlers
527 (create-static-file-dispatcher-and-handler
528 "/hunchentoot/test/image.jpg"
529 (make-pathname :name
"fz" :type
"jpg" :version nil
530 :defaults
*this-file
*)
532 (create-static-file-dispatcher-and-handler
533 "/hunchentoot/test/favicon.ico"
534 (make-pathname :name
"favicon" :type
"ico" :version nil
535 :defaults
*this-file
*))
536 (create-folder-dispatcher-and-handler
538 (make-pathname :name nil
:type nil
:version nil
539 :defaults
*this-file
*)
541 (mapcar (lambda (args)
542 (apply 'create-prefix-dispatcher args
))
543 '(("/hunchentoot/test/form-test.html" form-test
)
544 ("/hunchentoot/test/forbidden.html" forbidden
)
545 ("/hunchentoot/test/info.html" info
)
546 ("/hunchentoot/test/authorization.html" authorization-page
)
547 ("/hunchentoot/test/image-ram.jpg" image-ram-page
)
548 ("/hunchentoot/test/cookie.html" cookie-test
)
549 ("/hunchentoot/test/session.html" session-test
)
550 ("/hunchentoot/test/parameter_latin1_get.html" parameter-test-latin1-get
)
551 ("/hunchentoot/test/parameter_latin1_post.html" parameter-test-latin1-post
)
552 ("/hunchentoot/test/parameter_utf8_get.html" parameter-test-utf8-get
)
553 ("/hunchentoot/test/parameter_utf8_post.html" parameter-test-utf8-post
)
554 ("/hunchentoot/test/upload.html" upload-test
)
555 ("/hunchentoot/test/redir.html" redir
)
556 ("/hunchentoot/test/oops.html" oops
)
557 ("/hunchentoot/test/utf8-binary.txt" stream-direct
)
558 ("/hunchentoot/test/utf8-character.txt" stream-direct-utf-8
)
559 ("/hunchentoot/test/utf8-string.txt" stream-direct-utf-8-string
)
560 ("/hunchentoot/test/files/" send-file
)
561 ("/hunchentoot/test" menu
)))
562 (list 'default-dispatcher
)))