Hi,
So far I've got 48 create-static-file-dispatcher-and-handler definitions in my TBNL dispatch table. These are mostly image files now, but also CSS and javascript files. This number is about to go up by maybe five or six real soon now. Then soon after that I'll need to be able to serve an unknown, but large, number of files of different types.
Is there currently a dispatcher/handler that deals with directories, possibly even recursively, or maybe that consults some kind of list of things to serve? No point writing one if it is already done. If there isn't one already, I'll code one up and contribute it to TBNL.
Cheers, Bob
---- Bob Hutchison -- blogs at http://www.recursive.ca/hutch/ Recursive Design Inc. -- http://www.recursive.ca/
Hi Bob!
On Fri, 10 Jun 2005 09:17:23 -0400, Bob Hutchison hutch@recursive.ca wrote:
Is there currently a dispatcher/handler that deals with directories, possibly even recursively, or maybe that consults some kind of list of things to serve?
No, not yet.
No point writing one if it is already done. If there isn't one already, I'll code one up and contribute it to TBNL.
That would be nice.
TIA, Edi.
On 2005-06-10 09:17:23, Bob Hutchison wrote:
So far I've got 48 create-static-file-dispatcher-and-handler definitions in my TBNL dispatch table. These are mostly image files
Apache is really good for this task. You can either put your application (without the static files) into its own directory or the other way.
When you want to set the lisp-handler for the whole site except for one directory you could configure it that way:
... SetHandler lisp-handler <LocationMatch /static> SetHandler none </LocationMatch> ...
Regards, Stefan
On Fri, 10 Jun 2005 15:38:57 +0200, Stefan Scholl stesch@no-spoon.de wrote:
Apache is really good for this task.
Bob is the one who introduced backends other than Apache for TBNL so there's a slight chance he's not even using Apache... :)
Cheers, Edi.
Hi,
Sorry for the delay in replying (meetings all day so far, and on a Friday too)
Edi, you are correct :-) I am going straight at TBNL on linux and through Araneida on OS/X (but only because of a weird bug on OS/X that I cannot isolate).
An additional issue is that a lot of these files I have to serve are protected by the web application's security system -- some users are not allowed to get access to them, or there may be different versions for different audiences. I am pretty sure I can get Apache to handle this, but that would be work that duplicates functionality that is already done within the lisp application.
But you are right Stefan, Apache is good at this and would be a good way to go under other circumstances.
Cheers, Bob
On Jun 10, 2005, at 9:45 AM, Edi Weitz wrote:
On Fri, 10 Jun 2005 15:38:57 +0200, Stefan Scholl stesch@no-spoon.de wrote:
Apache is really good for this task.
Bob is the one who introduced backends other than Apache for TBNL so there's a slight chance he's not even using Apache... :)
Cheers, Edi. _______________________________________________ tbnl-devel site list tbnl-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/tbnl-devel
---- Bob Hutchison -- blogs at http://www.recursive.ca/hutch/ Recursive Design Inc. -- http://www.recursive.ca/
Hi,
I have implemented a handler for serving static files using TBNL. I ended up doing this differently than I expected. I extended create-prefix-dispatcher and called it create-prefix-dispatcher/2 and a little macro, prefix-dispatcher/2, to simplify things a bit. create-prefix-dispatcher/2 does everything that create-prefix-dispatcher does (in the same way) and is intended to be a drop-in replacement.
create-prefix-dispatcher/2 looks at its page-function argument and handles symbols exactly as create-prefix-dispatcher did, but cons are handled differently.
When create-prefix-dispatcher/2 encounters a page function that is a cons it assumes that it is a lambda with one argument that returns a lambda of no arguments. create-prefix-dispatcher/2 uses gensym to generate a symbol for the function then setfs its symbol-function to the lambda returned by (funcall (eval page-function) prefix). The generated symbol is then handled exactly as though that symbol was passed in as page-function. This seems a bit awkward to me, but it works.
The static-directory/*-handler takes 4 arguments: prefix -- the url prefix that matched the script-name. When this is removed from the front of the script-name, we are left with the path relative to the directory-path of the file requested. directory-path -- the root directory of the files to be served default-type -- the default mime-type (can be nil) file-type-map -- an assoc list of file name extensions (e.g. ".gif") and a mime type.
If the content-type cannot be determined it is not set (maybe not the best idea, but...)
I've not been able to test this on anything other that LWM using OS/X 10.3.9 -- so only tbnl-bivalent-streams has been tested at all.
Sorry, the documentation is a bit weak.
There is an example near the end of how to use this stuff.
Hope somebody finds this useful.
Cheers, Bob
-------
(defun static-directory/*-handler (prefix directory-path default-type file-type-map) "A TBNL handler that will serve static files located relative to a directory.
'prefix' is what TBNL matched to the script-name (this match provided the excuse to call this handler). If we remove the prefix from the front of the script-name we get the path, relative to 'directory-path', that identifies the file.
'default-type' is the default mime-type for the file, nil is okay.
'file-type-map' is an assoc list of file extensions and mime types. " (labels ((determine-content-type (relative-file-path) (or (cdr (find-if (lambda (pair) (zerop (mismatch (car pair) relative-file-path :from-end t))) file-type-map)) default-type))) (let* ((script-name (script-name)) (relative-file-path (subseq script-name (mismatch prefix script-name))) (path (concatenate 'string directory-path relative-file-path)) (time (or (file-write-date path) (get-universal-time))) (content-type (determine-content-type relative-file-path))) (when content-type (setf (content-type) content-type)) (unless (probe-file path) (setf (return-code) +http-not-found+) (throw 'tbnl-handler-done nil)) #+:tbnl-bivalent-streams (progn (handle-if-modified-since time) (with-open-file (file path :direction :input :element-type '(unsigned-byte 8) :if-does-not-exist nil) (let* ((len (file-length file)) (buf (make-array len :element-type '(unsigned-byte 8)))) (read-sequence buf file) (setf (header-out "Last-Modified") (rfc-1123-date time)) buf))) #-:tbnl-bivalent-streams (let ((buf (make-array 8192 :element-type 'character))) (handle-if-modified-since time) (let ((str (with-output-to-string (out) (with-open-file (file path :direction :input :if-does-not-exist nil) (loop for pos = (read-sequence buf file) until (zerop pos) do (write-sequence buf out :end pos)))))) (setf (header-out "Last-Modified") (rfc-1123-date time)) str)))))
(defmacro prefix-dispatcher/2 (fn &rest args) "construction a function with one argument, prefix, that returns a function of no arguments that calls fn with prefix as the first argument followed by the args." `(lambda (prefix) (lambda () (,fn prefix ,@args))))
(defun create-prefix-dispatcher/2 (prefix page-function) "Creates a dispatch function which will dispatch to the function denoted by PAGE-FUNCTION if the file name of the current request starts with the string PREFIX. This is exactly what create-prefix-dispatcher does. However, if page-function is a cons, then it must be of the form: (lambda (prefix) (lambda () ...)) This lambda serves as the page function."
(when (consp page-function) (let ((fn (gensym "handler")) (fv (funcall (eval page-function) prefix))) (setf (symbol-function fn) fv) (setf page-function fn))) (lambda (request) (let ((mismatch (mismatch (script-name request) prefix :test #'char=))) (and (or (null mismatch) (>= mismatch (length prefix))) page-function))))
(setq *dispatch-table* (nconc (mapcar (lambda (args) (apply #'create-prefix-dispatcher/2 args)) '(("/sienna/image/" (prefix-dispatcher/2 static-directory/*-handler "images/" nil '((".jpg" . "image/jpeg") (".jpeg" . "image/jpeg") (".gif" . "image/gif")))) ("/sienna/images/" (prefix-dispatcher/2 static-directory/*-handler "images/" nil '((".jpg" . "image/jpeg") (".jpeg" . "image/jpeg") (".gif" . "image/gif")))) ("/sienna/css/" (prefix-dispatcher/2 static-directory/*-handler "css/" nil '((".js" . "text/javascript") (".css" . "text/css") (".gif" . "image/gif")))) ("/sienna/item/" show-item))) (list #'default-dispatcher)))