Skip to content

Commit

Permalink
Have a callback for static file handlers.
Browse files Browse the repository at this point in the history
My usecase is to provide some caching headers by passing this
callback:

    (lambda (path content-type)
      (declare (ignore path content-type))
      (setf (hunchentoot:header-out "Cache-Control")
            "max-age=864000"))

Sadly there've been &optional args; so, to avoid mixing &optional and
&key args new function names are required.
  • Loading branch information
phmarek committed Dec 20, 2019
1 parent f3f70cc commit 2bd971c
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 16 deletions.
21 changes: 17 additions & 4 deletions docs/index.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1194,7 +1194,7 @@
</clix:function>

<clix:function name="create-folder-dispatcher-and-handler">
<clix:lambda-list>uri-prefix base-path <clix:lkw>optional</clix:lkw> content-type</clix:lambda-list>
<clix:lambda-list>uri-prefix base-path <clix:lkw>optional</clix:lkw> content-type callback</clix:lambda-list>
<clix:returns>dispatch-fn</clix:returns>
<clix:description>
Creates and returns a dispatch function which will dispatch to
Expand All @@ -1213,13 +1213,17 @@
the content type of each file will be
determined <a href="#handle-static-file">as usual</a>.
</p>
<p>
The <clix:arg>callback</clix:arg> is passed on to
<clix:ref>HANDLE-STATIC-FILE</clix:ref>.
</p>
</clix:description>
</clix:function>

<clix:function name='create-static-file-dispatcher-and-handler'>
<clix:lambda-list>uri path
<clix:lkw>optional
</clix:lkw> content-type
</clix:lkw> content-type callback
</clix:lambda-list>
<clix:returns>result
</clix:returns>
Expand All @@ -1230,6 +1234,10 @@
CONTENT-TYPE if the SCRIPT-NAME of the request matches the
string URI. If CONTENT-TYPE is NIL, tries to determine the
content type via the file&#039;s suffix.
<p>
The <clix:arg>callback</clix:arg> is passed on to
<clix:ref>HANDLE-STATIC-FILE</clix:ref>.
</p>
</clix:description>
</clix:function>

Expand Down Expand Up @@ -3263,7 +3271,7 @@
</clix:function>

<clix:function name="handle-static-file">
<clix:lambda-list>path <clix:lkw>optional</clix:lkw> content-type</clix:lambda-list>
<clix:lambda-list>path <clix:lkw>optional</clix:lkw> content-type callback</clix:lambda-list>
<clix:returns>nil</clix:returns>
<clix:description>
Sends the file denoted by the pathname designator
Expand All @@ -3272,11 +3280,16 @@
necessary handlers. In particular the function employs
<clix:ref>HANDLE-IF-MODIFIED-SINCE</clix:ref>.
<p>
If <clix:arg>content-type</clix:arg> is <code>NIL</code> the
If <clix:arg>content-type</clix:arg> is <code>NIL</code>, the
function tries to determine the correct content type from
the file's suffix or falls back
to <code>"application/octet-stream"</code> as a last resort.
</p>
<p>
The <clix:arg>callback</clix:arg> is run just before opening
the file; it can eg. check authorization or send caching headers.
It gets the filename and the (guessed) content-type as arguments.
</p>
<p>
Note that this function
calls <clix:ref>SEND-HEADERS</clix:ref> internally, so after
Expand Down
25 changes: 17 additions & 8 deletions misc.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,14 @@ had returned RESULT. See the source code of REDIRECT for an example."
(header-out :content-range) (format nil "bytes ~D-~D/~D" start end (file-length file))))
bytes-to-send))

(defun handle-static-file (pathname &optional content-type)
(defun handle-static-file (pathname &optional content-type callback)
"A function which acts like a Hunchentoot handler for the file
denoted by PATHNAME. Sends a content type header corresponding to
CONTENT-TYPE or \(if that is NIL) tries to determine the content type
via the file's suffix."
via the suffix of the file.
CALLBACK is run just before sending the file, and can be used
to set headers or check authorization;
arguments are the filename and the (guessed) content-type."
(when (or (wild-pathname-p pathname)
(not (fad:file-exists-p pathname))
(fad:directory-exists-p pathname))
Expand All @@ -170,6 +173,8 @@ via the file's suffix."
(header-out :last-modified) (rfc-1123-date time)
(header-out :accept-ranges) "bytes")
(handle-if-modified-since time)
(unless (null callback)
(funcall callback pathname content-type))
(with-open-file (file pathname
:direction :input
:element-type 'octet)
Expand All @@ -187,26 +192,28 @@ via the file's suffix."
(decf bytes-to-send chunk-size)))
(finish-output out)))))

(defun create-static-file-dispatcher-and-handler (uri path &optional content-type)
(defun create-static-file-dispatcher-and-handler (uri path &optional content-type callback)
"Creates and returns a request dispatch function which will dispatch
to a handler function which emits the file denoted by the pathname
designator PATH with content type CONTENT-TYPE if the SCRIPT-NAME of
the request matches the string URI. If CONTENT-TYPE is NIL, tries to
determine the content type via the file's suffix."
determine the content type via the file's suffix.
See HANDLE-STATIC-FILE for CALLBACK."
;; the dispatcher
(lambda (request)
(when (string= (script-name request) uri)
;; the handler
(lambda ()
(handle-static-file path content-type)))))
(handle-static-file path content-type callback)))))

(defun create-folder-dispatcher-and-handler (uri-prefix base-path &optional content-type)
(defun create-folder-dispatcher-and-handler (uri-prefix base-path &key content-type callback)
"Creates and returns a dispatch function which will dispatch to a
handler function which emits the file relative to BASE-PATH that is
denoted by the URI of the request relative to URI-PREFIX. URI-PREFIX
must be a string ending with a slash, BASE-PATH must be a pathname
designator for an existing directory. If CONTENT-TYPE is not NIL,
it'll be the content type used for all files in the folder."
it'll be the content type used for all files in the folder.
See HANDLE-STATIC-FILE for CALLBACK."
(unless (and (stringp uri-prefix)
(plusp (length uri-prefix))
(char= (char uri-prefix (1- (length uri-prefix))) #\/))
Expand All @@ -218,7 +225,9 @@ it'll be the content type used for all files in the folder."
(when (null request-path)
(setf (return-code*) +http-forbidden+)
(abort-request-handler))
(handle-static-file (merge-pathnames request-path base-path) content-type))))
(handle-static-file (merge-pathnames request-path base-path)
content-type
callback))))
(create-prefix-dispatcher uri-prefix #'handler)))

(defun no-cache ()
Expand Down
21 changes: 17 additions & 4 deletions www/hunchentoot-doc.html
Original file line number Diff line number Diff line change
Expand Up @@ -1165,7 +1165,7 @@ <h4 xmlns=""><a name="easy-handlers">Using the easy-handler framework</a></h4>

<p xmlns=""><a class="none" name="create-folder-dispatcher-and-handler"></a>
[Function]
<br><b>create-folder-dispatcher-and-handler</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">uri-prefix base-path <tt>&amp;optional</tt> content-type</clix:lambda-list></i>
<br><b>create-folder-dispatcher-and-handler</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">uri-prefix base-path <tt>&amp;optional</tt> content-type callback</clix:lambda-list></i>
=&gt;
<i><clix:returns xmlns:clix="http://bknr.net/clixdoc">dispatch-fn</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
Creates and returns a dispatch function which will dispatch to
Expand All @@ -1184,13 +1184,17 @@ <h4 xmlns=""><a name="easy-handlers">Using the easy-handler framework</a></h4>
the content type of each file will be
determined <a href="#handle-static-file">as usual</a>.
</p>
<p xmlns="http://www.w3.org/1999/xhtml">
The <code xmlns=""><i>callback</i></code> is passed on to
<code xmlns=""><a href="#handle-static-file">HANDLE-STATIC-FILE</a></code>.
</p>
</clix:description></blockquote></p>

<p xmlns=""><a class="none" name="create-static-file-dispatcher-and-handler"></a>
[Function]
<br><b>create-static-file-dispatcher-and-handler</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">uri path
<tt>&amp;optional
</tt> content-type
</tt> content-type callback
</clix:lambda-list></i>
=&gt;
<i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
Expand All @@ -1201,6 +1205,10 @@ <h4 xmlns=""><a name="easy-handlers">Using the easy-handler framework</a></h4>
CONTENT-TYPE if the SCRIPT-NAME of the request matches the
string URI. If CONTENT-TYPE is NIL, tries to determine the
content type via the file's suffix.
<p xmlns="http://www.w3.org/1999/xhtml">
The <code xmlns=""><i>callback</i></code> is passed on to
<code xmlns=""><a href="#handle-static-file">HANDLE-STATIC-FILE</a></code>.
</p>
</clix:description></blockquote></p>

<p xmlns=""><a class="none" name="define-easy-handler"></a>
Expand Down Expand Up @@ -3143,7 +3151,7 @@ <h4 xmlns=""><a name="misc">Miscellaneous</a></h4>

<p xmlns=""><a class="none" name="handle-static-file"></a>
[Function]
<br><b>handle-static-file</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">path <tt>&amp;optional</tt> content-type</clix:lambda-list></i>
<br><b>handle-static-file</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">path <tt>&amp;optional</tt> content-type callback</clix:lambda-list></i>
=&gt;
<i><clix:returns xmlns:clix="http://bknr.net/clixdoc">nil</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
Sends the file denoted by the pathname designator
Expand All @@ -3152,11 +3160,16 @@ <h4 xmlns=""><a name="misc">Miscellaneous</a></h4>
necessary handlers. In particular the function employs
<code><a href="#handle-if-modified-since">HANDLE-IF-MODIFIED-SINCE</a></code>.
<p xmlns="http://www.w3.org/1999/xhtml">
If <code xmlns=""><i>content-type</i></code> is <code>NIL</code> the
If <code xmlns=""><i>content-type</i></code> is <code>NIL</code>, the
function tries to determine the correct content type from
the file's suffix or falls back
to <code>"application/octet-stream"</code> as a last resort.
</p>
<p xmlns="http://www.w3.org/1999/xhtml">
The <code xmlns=""><i>callback</i></code> is run just before opening
the file; it can eg. check authorization or send caching headers.
It gets the filename and the (guessed) content-type as arguments.
</p>
<p xmlns="http://www.w3.org/1999/xhtml">
Note that this function
calls <code xmlns=""><a href="#send-headers">SEND-HEADERS</a></code> internally, so after
Expand Down

0 comments on commit 2bd971c

Please sign in to comment.