diff --git a/CHANGELOG b/CHANGELOG index d8ce7bb..3fa2c6e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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. diff --git a/docs/index.xml b/docs/index.xml index ec97f0b..08af58e 100644 --- a/docs/index.xml +++ b/docs/index.xml @@ -1194,7 +1194,7 @@ - uri-prefix base-path optional content-type + uri-prefix base-path optional content-type callback dispatch-fn Creates and returns a dispatch function which will dispatch to @@ -1213,13 +1213,17 @@ the content type of each file will be determined as usual.

+

+ The callback is passed on to + HANDLE-STATIC-FILE. +

uri path optional - content-type + content-type callback result @@ -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's suffix. +

+ The callback is passed on to + HANDLE-STATIC-FILE. +

@@ -3263,7 +3271,7 @@ - path optional content-type + path optional content-type callback nil Sends the file denoted by the pathname designator @@ -3272,11 +3280,16 @@ necessary handlers. In particular the function employs HANDLE-IF-MODIFIED-SINCE.

- If content-type is NIL the + If content-type is NIL, the function tries to determine the correct content type from the file's suffix or falls back to "application/octet-stream" as a last resort.

+

+ The callback 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. +

Note that this function calls SEND-HEADERS internally, so after @@ -3353,6 +3366,18 @@ + + + + system-area-pointer + + + Returns a SAP to the certificate the peer used to authenticate itself, or NIL. + Can be used with CL+SSL:CERTIFICATE-SUBJECT-COMMON-NAMES and the other certificate functions. + + + + return-code diff --git a/misc.lisp b/misc.lisp index bc7a640..c2b05f3 100644 --- a/misc.lisp +++ b/misc.lisp @@ -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)) @@ -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) @@ -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))) #\/)) @@ -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 () diff --git a/packages.lisp b/packages.lisp index ded51a3..19035da 100644 --- a/packages.lisp +++ b/packages.lisp @@ -274,6 +274,7 @@ #:single-threaded-taskmaster #-:hunchentoot-no-ssl #:ssl-acceptor #:ssl-p + #:get-peer-ssl-certificate #:start #:start-listening #:start-session diff --git a/request.lisp b/request.lisp index e8c6312..9031a79 100644 --- a/request.lisp +++ b/request.lisp @@ -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)))) @@ -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 @@ -311,7 +317,7 @@ 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")) @@ -319,17 +325,21 @@ unknown character set ~A in request content type." (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*)) diff --git a/ssl.lisp b/ssl.lisp index 58ecc2e..58da9c9 100644 --- a/ssl.lisp +++ b/ssl.lisp @@ -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*)) diff --git a/www/hunchentoot-doc.html b/www/hunchentoot-doc.html index f69970b..aad4ae0 100644 --- a/www/hunchentoot-doc.html +++ b/www/hunchentoot-doc.html @@ -1165,7 +1165,7 @@

Using the easy-handler framework

[Function] -
create-folder-dispatcher-and-handler uri-prefix base-path &optional content-type +
create-folder-dispatcher-and-handler uri-prefix base-path &optional content-type callback => dispatch-fn

Creates and returns a dispatch function which will dispatch to @@ -1184,13 +1184,17 @@

Using the easy-handler framework

the content type of each file will be determined as usual.

+

+ The callback is passed on to + HANDLE-STATIC-FILE. +

[Function]
create-static-file-dispatcher-and-handler uri path &optional - content-type + content-type callback => result @@ -1201,6 +1205,10 @@

Using the easy-handler framework

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. +

+ The callback is passed on to + HANDLE-STATIC-FILE. +

@@ -3143,7 +3151,7 @@

Miscellaneous

[Function] -
handle-static-file path &optional content-type +
handle-static-file path &optional content-type callback => nil

Sends the file denoted by the pathname designator @@ -3152,11 +3160,16 @@

Miscellaneous

necessary handlers. In particular the function employs HANDLE-IF-MODIFIED-SINCE.

- If content-type is NIL the + If content-type is NIL, the function tries to determine the correct content type from the file's suffix or falls back to "application/octet-stream" as a last resort.

+

+ The callback 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. +

Note that this function calls SEND-HEADERS internally, so after @@ -3232,6 +3245,18 @@

Miscellaneous

Whether the current connection to the client is secure. See ACCEPTOR-SSL-P.

+

+ [Function] +
ssl-get-peer-certificate + + => + system-area-pointer +

+ Returns a SAP to the certificate the peer used to authenticate itself, or NIL. + Can be used with CL+SSL:CERTIFICATE-SUBJECT-COMMON-NAMES and the other certificate functions. +

+ +

[Function]
reason-phrase return-code @@ -4160,6 +4185,9 @@

Symbol index

ssl-acceptorStandard class
  • +ssl-get-peer-certificateFunction +
  • +
  • ssl-pFunction