Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Three updates for HT. #170

Merged
merged 3 commits into from
Jan 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
2019-05-22
Respect the "Content-Length" header when parsing POST parameters
Fix receiving a 0-byte file (where not even the MIME boundaries are sent)

Version 1.2.38
2017-12-03
Better pathname validation.
Expand Down
33 changes: 29 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 Expand Up @@ -3353,6 +3366,18 @@
</clix:description>
</clix:function>

<clix:function name='ssl-get-peer-certificate'>
<clix:lambda-list>
</clix:lambda-list>
<clix:returns>system-area-pointer
</clix:returns>
<clix:description>
Returns a SAP to the certificate the peer used to authenticate itself, or NIL.
Can be used with <code>CL+SSL:CERTIFICATE-SUBJECT-COMMON-NAMES</code> and the other certificate functions.
</clix:description>
</clix:function>


<clix:function name='reason-phrase'>
<clix:lambda-list>return-code
</clix:lambda-list>
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 &optional 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
1 change: 1 addition & 0 deletions packages.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@
#:single-threaded-taskmaster
#-:hunchentoot-no-ssl #:ssl-acceptor
#:ssl-p
#:get-peer-ssl-certificate
#:start
#:start-listening
#:start-session
Expand Down
56 changes: 33 additions & 23 deletions request.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,12 @@ slot values are computed in this :AFTER method."
content type has already been verified. Returns the form data as
alist or NIL if there was no data or the data could not be parsed."
(handler-case*
(let ((content-stream (make-flexi-stream (content-stream request) :external-format +latin-1+)))
(let* ((content-length (header-in :content-length request))
(content-stream (make-flexi-stream (content-stream request)
:external-format +latin-1+
:bound (if content-length
(parse-integer content-length
:junk-allowed T)))))
(prog1
(parse-rfc2388-form-data content-stream (header-in :content-type request) external-format)
(let ((stray-data (get-post-data :already-read (flexi-stream-position content-stream))))
Expand All @@ -289,20 +294,21 @@ EXTERNAL-FORMAT specifies the external format of the data in the
request body. By default, the encoding is determined from the
Content-Type header of the request or from
*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT* if none is found."
(when (and (header-in :content-type request)
(member (request-method request) *methods-for-post-parameters* :test #'eq)
(or force
(not (slot-value request 'raw-post-data)))
;; can't reparse multipart posts, even when FORCEd
(not (eq t (slot-value request 'raw-post-data))))
(unless (or (header-in :content-length request)
(input-chunking-p))
(log-message* :warning "Can't read request body because there's ~
(let* ((content-length (header-in :content-length request)))
(when (and (header-in :content-type request)
(member (request-method request) *methods-for-post-parameters* :test #'eq)
(or force
(not (slot-value request 'raw-post-data)))
;; can't reparse multipart posts, even when FORCEd
(not (eq t (slot-value request 'raw-post-data))))
(unless (or content-length
(input-chunking-p))
(log-message* :warning "Can't read request body because there's ~
no Content-Length header and input chunking is off.")
(return-from maybe-read-post-parameters nil))
(handler-case*
(return-from maybe-read-post-parameters nil))
(handler-case*
(multiple-value-bind (type subtype charset)
(parse-content-type (header-in :content-type request))
(parse-content-type (header-in :content-type request))
(let ((external-format (or external-format
(when charset
(handler-case
Expand All @@ -311,25 +317,29 @@ no Content-Length header and input chunking is off.")
(hunchentoot-warn "Ignoring ~
unknown character set ~A in request content type."
charset))))
*hunchentoot-default-external-format*)))
*hunchentoot-default-external-format*)))
(setf (slot-value request 'post-parameters)
(cond ((and (string-equal type "application")
(string-equal subtype "x-www-form-urlencoded"))
(form-url-encoded-list-to-alist
(split "&" (raw-post-data :request request :external-format +latin-1+))
external-format))
((and (string-equal type "multipart")
(string-equal subtype "form-data"))
(string-equal subtype "form-data")
(if content-length
(plusp (parse-integer content-length
:junk-allowed T))
T))
(prog1 (parse-multipart-form-data request external-format)
(setf (slot-value request 'raw-post-data) t)))))))
(error (condition)
(log-message* :error "Error when reading POST parameters from body: ~A" condition)
;; this is not the right thing to do because it could happen
;; that we aren't finished reading from the request stream and
;; can't send a reply - to be revisited
(setf (return-code*) +http-bad-request+
*finish-processing-socket* t)
(abort-request-handler)))))
(error (condition)
(log-message* :error "Error when reading POST parameters from body: ~A" condition)
;; this is not the right thing to do because it could happen
;; that we aren't finished reading from the request stream and
;; can't send a reply - to be revisited
(setf (return-code*) +http-bad-request+
*finish-processing-socket* t)
(abort-request-handler))))))

(defun recompute-request-parameters (&key (request *request*)
(external-format *hunchentoot-default-external-format*))
Expand Down
4 changes: 4 additions & 0 deletions ssl.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,7 @@ stream."
:certificate-file (acceptor-ssl-certificate-file acceptor)
:privatekey-file (acceptor-ssl-privatekey-file acceptor)
:privatekey-password (acceptor-ssl-privatekey-password acceptor))))


(defun get-peer-ssl-certificate ()
(cl+ssl:ssl-stream-x509-certificate *hunchentoot-stream*))
36 changes: 32 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 Expand Up @@ -3232,6 +3245,18 @@ <h4 xmlns=""><a name="misc">Miscellaneous</a></h4>
Whether the current connection to the client is secure. See <code><a href="#acceptor-ssl-p">ACCEPTOR-SSL-P</a></code>.
</clix:description></blockquote></p>

<p xmlns=""><a class="none" name="ssl-get-peer-certificate"></a>
[Function]
<br><b>ssl-get-peer-certificate</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
</clix:lambda-list></i>
=&gt;
<i><clix:returns xmlns:clix="http://bknr.net/clixdoc">system-area-pointer
</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
Returns a SAP to the certificate the peer used to authenticate itself, or NIL.
Can be used with <code xmlns="http://www.w3.org/1999/xhtml">CL+SSL:CERTIFICATE-SUBJECT-COMMON-NAMES</code> and the other certificate functions.
</clix:description></blockquote></p>


<p xmlns=""><a class="none" name="reason-phrase"></a>
[Function]
<br><b>reason-phrase</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">return-code
Expand Down Expand Up @@ -4160,6 +4185,9 @@ <h3 xmlns=""><a class="none" name="index">Symbol index</a></h3>
<code><a href="#ssl-acceptor">ssl-acceptor</a></code><span class="entry-type">Standard class</span>
</li>
<li>
<code><a href="#ssl-get-peer-certificate">ssl-get-peer-certificate</a></code><span class="entry-type">Function</span>
</li>
<li>
<code><a href="#ssl-p">ssl-p</a></code><span class="entry-type">Function</span>
</li>
<li>
Expand Down