Revision: 4647
Author: hans
URL: http://bknr.net/trac/changeset/4647
add html version of documentation
A trunk/thirdparty/hunchentoot/www/hunchentoot-doc.html
Change set too large, please see URL above
Revision: 4645
Author: hans
URL: http://bknr.net/trac/changeset/4645
Patch from Dan Weinreb:
limit the maximum number of request handler threads that a taskmaster may
create.
Support a new SOFT argument to the STOP function that makes Hunchentoot
wait until all currently outstanding requests have been processed before
returning.
Add a favicon.ico to the standard file tree.
Refactorings in the START-OUTPUT function to separate out the actual
header sending from the request processing part. The new SEND-RESPONSE
function does the I/O part of sending the response header (and possibly
content) and also does the logging. Used by the thread count limiting
code to send a 503 message if no more threads can be created.
U trunk/thirdparty/hunchentoot/acceptor.lisp
U trunk/thirdparty/hunchentoot/compat.lisp
U trunk/thirdparty/hunchentoot/headers.lisp
U trunk/thirdparty/hunchentoot/taskmaster.lisp
U trunk/thirdparty/hunchentoot/util.lisp
A trunk/thirdparty/hunchentoot/www/favicon.ico
Change set too large, please see URL above
Revision: 4644
Author: hans
URL: http://bknr.net/trac/changeset/4644
Add skeleton file tree to be served by Hunchentoot in its default
configuration.
Make error pages customizable through files.
Add new :document-root argument to acceptor to specify where files should
be served from.
U trunk/thirdparty/hunchentoot/acceptor.lisp
U trunk/thirdparty/hunchentoot/doc/index.xml
U trunk/thirdparty/hunchentoot/easy-handlers.lisp
U trunk/thirdparty/hunchentoot/misc.lisp
A trunk/thirdparty/hunchentoot/www/
A trunk/thirdparty/hunchentoot/www/errors/
A trunk/thirdparty/hunchentoot/www/errors/404.html
A trunk/thirdparty/hunchentoot/www/img/
A trunk/thirdparty/hunchentoot/www/img/made-with-lisp-logo.jpg
A trunk/thirdparty/hunchentoot/www/index.html
Modified: trunk/thirdparty/hunchentoot/acceptor.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/acceptor.lisp 2011-02-07 17:42:05 UTC (rev 4643)
+++ trunk/thirdparty/hunchentoot/acceptor.lisp 2011-02-09 17:07:08 UTC (rev 4644)
@@ -29,6 +29,10 @@
(in-package :hunchentoot)
+(eval-when (:load-toplevel)
+ (defun default-document-directory (&optional sub-directory)
+ (asdf:system-relative-pathname :hunchentoot (format nil "www/~@[~A~]" sub-directory))))
+
(defclass acceptor ()
((port :initarg :port
:reader acceptor-port
@@ -124,7 +128,20 @@
:documentation "Pathname of the server error
log file which is used to log informational,
warning and error messages in a free-text
-format intended for human inspection"))
+format intended for human inspection")
+ (error-template-directory :initarg :error-template-directory
+ :accessor acceptor-error-template-directory
+ :documentation "Directory pathname that
+ contains error message template files for server-generated error
+ messages. Files must be named <return-code>.html with <return-code>
+ representing the HTTP return code that the file applies to,
+ i.e. 404.html would be used as the content for a HTTP 404 Not found
+ response.")
+ (document-root :initarg :document-root
+ :accessor acceptor-document-root
+ :documentation "Directory pathname that points to
+files that are served by the acceptor if no more specific
+acceptor-dispatch-request method handles the request."))
(:default-initargs
:address nil
:port 80
@@ -139,7 +156,9 @@
:read-timeout *default-connection-timeout*
:write-timeout *default-connection-timeout*
:access-log-pathname nil
- :message-log-pathname nil)
+ :message-log-pathname nil
+ :document-root (load-time-value (default-document-directory))
+ :error-template-directory (load-time-value (default-document-directory "errors/")))
(:documentation "To create a Hunchentoot webserver, you make an
instance of this class and use the generic function START to start it
\(and STOP to stop it). Use the :PORT initarg if you don't want to
@@ -457,7 +476,12 @@
(defmethod acceptor-dispatch-request ((acceptor acceptor) request)
"Detault implementation of the request dispatch method, generates a +http-not-found+ error+."
(declare (ignore request))
- (setf (return-code *reply*) +http-not-found+))
+ (if (acceptor-document-root acceptor)
+ (handle-static-file (merge-pathnames (if (equal (script-name*) "/")
+ "index.html"
+ (subseq (script-name*) 1))
+ (acceptor-document-root acceptor)))
+ (setf (return-code *reply*) +http-not-found+)))
(defmethod handle-request ((*acceptor* acceptor) (*request* request))
"Standard method for request handling. Calls the request dispatcher
@@ -493,41 +517,72 @@
client. For other return codes, the content can be ignored and/or
processed, depending on the requirements of the acceptor class.
Note that the CONTENT argument can be NIL if the handler wants to
- send the data to the client stream itself."))
+ send the data to the client stream itself.
+ If an ERROR-TEMPLATE-DIRECTORY is set in the current acceptor and
+ the directory contains a file corresponding to HTTP-RETURN-CODE,
+ that file is sent to the client after variable substitution.
+ Variables are referenced by ${<variable-name>}. Currently, only
+ the ${script-name} variable is supported which contains the current
+ URL relative to the server's base URL."))
+
(defmethod acceptor-handle-return-code ((acceptor acceptor) http-return-code content)
"Default function to generate error message sent to the client."
- (flet ((cooked-message (format &rest arguments)
- (setf (content-type*) "text/html; charset=iso-8859-1")
- (format nil "<html><head><title>~D ~A</title></head><body><h1>~:*~A</h1>~?<p><hr>~A</p></body></html>"
- http-return-code (reason-phrase http-return-code)
- format (mapcar (lambda (arg)
- (if (stringp arg)
- (escape-for-html arg)
- arg))
- arguments)
- (address-string))))
- (case http-return-code
- ((#.+http-internal-server-error+
- #.+http-ok+)
- content)
- ((#.+http-moved-temporarily+
- #.+http-moved-permanently+)
- (cooked-message "The document has moved <a href='~A'>here</a>" (header-out :location)))
- ((#.+http-authorization-required+)
- (cooked-message "The server could not verify that you are authorized to access the document requested. ~
+ (labels
+ ((cooked-message (format &rest arguments)
+ (setf (content-type*) "text/html; charset=iso-8859-1")
+ (format nil "<html><head><title>~D ~A</title></head><body><h1>~:*~A</h1>~?<p><hr>~A</p></body></html>"
+ http-return-code (reason-phrase http-return-code)
+ format (mapcar (lambda (arg)
+ (if (stringp arg)
+ (escape-for-html arg)
+ arg))
+ arguments)
+ (address-string)))
+ (substitute-request-context-variables (string)
+ (cl-ppcre:regex-replace-all "(?i)\\$\\{([a-z0-9-_]+)\\}"
+ string
+ (lambda (target-string start end match-start match-end reg-starts reg-ends)
+ (declare (ignore start end match-start match-end))
+ (let ((variable (intern (string-upcase (subseq target-string
+ (aref reg-starts 0)
+ (aref reg-ends 0)))
+ :keyword)))
+ (case variable
+ (:script-name (script-name*))
+ (otherwise (string variable)))))))
+ (file-contents (file)
+ (let ((buf (make-string (file-length file))))
+ (read-sequence buf file)
+ buf))
+ (error-contents-from-template ()
+ (let ((error-file-template-pathname (and (acceptor-error-template-directory acceptor)
+ (probe-file (make-pathname :name (princ-to-string http-return-code)
+ :type "html"
+ :defaults (acceptor-error-template-directory acceptor))))))
+ (when error-file-template-pathname
+ (with-open-file (file error-file-template-pathname :if-does-not-exist nil :element-type 'character)
+ (when file
+ (substitute-request-context-variables (file-contents file))))))))
+ (or (error-contents-from-template)
+ (case http-return-code
+ ((#.+http-moved-temporarily+
+ #.+http-moved-permanently+)
+ (cooked-message "The document has moved <a href='~A'>here</a>" (header-out :location)))
+ ((#.+http-authorization-required+)
+ (cooked-message "The server could not verify that you are authorized to access the document requested. ~
Either you supplied the wrong credentials \(e.g., bad password), or your browser doesn't ~
understand how to supply the credentials required."))
- ((#.+http-forbidden+)
- (cooked-message "You don't have permission to access ~A on this server."
- (script-name *request*)))
- ((#.+http-not-found+)
- (cooked-message "The requested URL ~A was not found on this server."
- (script-name *request*)))
- ((#.+http-bad-request+)
- (cooked-message "Your browser sent a request that this server could not understand."))
- (otherwise
- content))))
+ ((#.+http-forbidden+)
+ (cooked-message "You don't have permission to access ~A on this server."
+ (script-name *request*)))
+ ((#.+http-not-found+)
+ (cooked-message "The requested URL ~A was not found on this server."
+ (script-name *request*)))
+ ((#.+http-bad-request+)
+ (cooked-message "Your browser sent a request that this server could not understand."))
+ (otherwise
+ content)))))
(defgeneric acceptor-remove-session (acceptor session)
(:documentation
Modified: trunk/thirdparty/hunchentoot/doc/index.xml
===================================================================
--- trunk/thirdparty/hunchentoot/doc/index.xml 2011-02-07 17:42:05 UTC (rev 4643)
+++ trunk/thirdparty/hunchentoot/doc/index.xml 2011-02-09 17:07:08 UTC (rev 4644)
@@ -475,10 +475,10 @@
</clix:returns>
</clix:listed-accessor>
- <clix:listed-accessor generic='true' name='acceptor-request-dispatcher'>
+ <clix:listed-accessor generic='true' name='acceptor-error-template-directory'>
<clix:lambda-list>acceptor
</clix:lambda-list>
- <clix:returns>request-dispatcher
+ <clix:returns>(or pathname null)
</clix:returns>
</clix:listed-accessor>
@@ -531,6 +531,13 @@
</clix:returns>
</clix:listed-accessor>
+ <clix:listed-accessor generic='true' name='acceptor-request-dispatcher'>
+ <clix:lambda-list>acceptor
+ </clix:lambda-list>
+ <clix:returns>request-dispatcher
+ </clix:returns>
+ </clix:listed-accessor>
+
<clix:description>
These are accessors for various slots of <clix:ref>ACCEPTOR</clix:ref>
objects. See the docstrings of these slots for more information and
@@ -685,6 +692,32 @@
</clix:description>
</clix:function>
+ <clix:function name="acceptor-handle-return-code" generic="true">
+ <clix:lambda-list>acceptor http-return-code content</clix:lambda-list>
+ <clix:description>
+ This function is called after the request's handler has been
+ invoked, before starting to send any output to the client. It
+ converts the HTTP return code that has been determined as the
+ result of the handler invocation into a content body sent to
+ the user. The content generated by the handler is passed to
+ this function as <clix:arg>CONTENT</clix:arg> argument. For
+ positive return codes (i.e. ``200 OK''), the CONTENT is
+ typically just sent to the client. For other return codes,
+ the content can be ignored and/or processed, depending on the
+ requirements of the acceptor class. Note that the
+ <clix:arg>CONTENT</clix:arg> argument can be NIL if the
+ handler wants to send the data to the client stream itself.
+
+ If an ERROR-TEMPLATE-DIRECTORY is set in the current acceptor
+ and the directory contains a file corresponding to
+ <clix:arg>HTTP-RETURN-CODE</clix:arg>, that file is sent to
+ the client after variable substitution. Variables are
+ referenced by ${<variable-name>}. Currently, only the
+ ${script-name} variable is supported which contains the
+ current URL relative to the server's base URL.
+ </clix:description>
+ </clix:function>
+
</clix:subchapter>
<clix:subchapter name="taskmasters" title="Taskmasters">
Modified: trunk/thirdparty/hunchentoot/easy-handlers.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/easy-handlers.lisp 2011-02-07 17:42:05 UTC (rev 4643)
+++ trunk/thirdparty/hunchentoot/easy-handlers.lisp 2011-02-09 17:07:08 UTC (rev 4644)
@@ -29,7 +29,7 @@
(in-package :hunchentoot)
-(defvar *dispatch-table* (list 'dispatch-easy-handlers 'default-dispatcher)
+(defvar *dispatch-table* (list 'dispatch-easy-handlers)
"A global list of dispatch functions.")
(defvar *easy-handler-alist* nil
@@ -339,4 +339,4 @@
(loop for dispatcher in *dispatch-table*
for action = (funcall dispatcher request)
when action return (funcall action)
- finally (setf (return-code *reply*) +http-not-found+)))
+ finally (call-next-method)))
Modified: trunk/thirdparty/hunchentoot/misc.lisp
===================================================================
--- trunk/thirdparty/hunchentoot/misc.lisp 2011-02-07 17:42:05 UTC (rev 4643)
+++ trunk/thirdparty/hunchentoot/misc.lisp 2011-02-09 17:07:08 UTC (rev 4644)
@@ -145,28 +145,29 @@
bytes-to-send (1+ (- end start))))
bytes-to-send))
-(defun handle-static-file (path &optional content-type)
+(defun handle-static-file (pathname &optional content-type)
"A function which acts like a Hunchentoot handler for the file
-denoted by PATH. Sends a content type header corresponding to
+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."
- (when (or (wild-pathname-p path)
- (not (fad:file-exists-p path))
- (fad:directory-exists-p path))
+ (when (or (wild-pathname-p pathname)
+ (not (fad:file-exists-p pathname))
+ (fad:directory-exists-p pathname))
;; file does not exist
(setf (return-code*) +http-not-found+)
(abort-request-handler))
- (let ((time (or (file-write-date path) (get-universal-time)))
+ (let ((time (or (file-write-date pathname)
+ (get-universal-time)))
bytes-to-send)
- (setf (content-type*) (or content-type
- (mime-type path)
- "application/octet-stream"))
(handle-if-modified-since time)
- (with-open-file (file path
- :direction :input
- :element-type 'octet
- :if-does-not-exist nil)
- (setf (header-out :content-range) (format nil "bytes 0-~D/*" (file-length file))
+ (with-open-file (file pathname
+ :direction :input
+ :element-type 'octet
+ :if-does-not-exist nil)
+ (setf (content-type*) (or content-type
+ (mime-type pathname)
+ "application/octet-stream")
+ (header-out :content-range) (format nil "bytes 0-~D/*" (file-length file))
(header-out :last-modified) (rfc-1123-date time)
bytes-to-send (maybe-handle-range-header file)
(content-length*) bytes-to-send)
Added: trunk/thirdparty/hunchentoot/www/errors/404.html
===================================================================
--- trunk/thirdparty/hunchentoot/www/errors/404.html (rev 0)
+++ trunk/thirdparty/hunchentoot/www/errors/404.html 2011-02-09 17:07:08 UTC (rev 4644)
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Not found</title>
+ </head>
+ <body>
+ Resource ${script-name} not found.
+ <img src="/img/made-with-lisp-logo.jpg" width="300" height="100"/>
+ </body>
+</html>
Added: trunk/thirdparty/hunchentoot/www/img/made-with-lisp-logo.jpg
===================================================================
(Binary files differ)
Property changes on: trunk/thirdparty/hunchentoot/www/img/made-with-lisp-logo.jpg
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/thirdparty/hunchentoot/www/index.html
===================================================================
--- trunk/thirdparty/hunchentoot/www/index.html (rev 0)
+++ trunk/thirdparty/hunchentoot/www/index.html 2011-02-09 17:07:08 UTC (rev 4644)
@@ -0,0 +1,17 @@
+<html>
+ <head>
+ <title>Welcome to Hunchentoot!</title>
+ </head>
+ <body>
+ <h1>Welcome</h1>
+ <p>
+ When you're reading this message, Hunchentoot has been properly installed.
+ </p>
+ <p>
+ Please read the <a href="../doc/index.html">documentation</a>.
+ </p>
+ <p>
+ <img src="img/made-with-lisp-logo.jpg" width="300" height="100"/>
+ </p>
+ </body>
+</html>
Revision: 4643
Author: hans
URL: http://bknr.net/trac/changeset/4643
Numerous changes. Please note that startup has changed:
(hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 4242))
Please test Hunchentoot and report problems:
(hunchentoot-test:test-hunchentoot "http://localhost:4242")
Summary of changes:
Overhaul error and return code handling. Improve behavior for internal errors.
Support Range: header in static file handler.
Use generic functions instead of closures in core functionality:
acceptor-server-name, acceptor-remove-session, acceptor-dispatch-request,
acceptor-handle-return-code
I attempted to document all changes, but please report if there are
any missing bits.
_U trunk/thirdparty/hunchentoot/
U trunk/thirdparty/hunchentoot/acceptor.lisp
U trunk/thirdparty/hunchentoot/doc/index.xml
U trunk/thirdparty/hunchentoot/easy-handlers.lisp
U trunk/thirdparty/hunchentoot/headers.lisp
U trunk/thirdparty/hunchentoot/misc.lisp
U trunk/thirdparty/hunchentoot/packages.lisp
U trunk/thirdparty/hunchentoot/request.lisp
U trunk/thirdparty/hunchentoot/session.lisp
U trunk/thirdparty/hunchentoot/specials.lisp
_U trunk/thirdparty/hunchentoot/test/
U trunk/thirdparty/hunchentoot/test/script-engine.lisp
U trunk/thirdparty/hunchentoot/test/script.lisp
U trunk/thirdparty/hunchentoot/test/test-handlers.lisp
U trunk/thirdparty/hunchentoot/util.lisp
Change set too large, please see URL above
Revision: 4642
Author: hans
URL: http://bknr.net/trac/changeset/4642
support Range: header to retrieve partial resources
U trunk/thirdparty/drakma/doc/index.html
U trunk/thirdparty/drakma/request.lisp
Modified: trunk/thirdparty/drakma/doc/index.html
===================================================================
--- trunk/thirdparty/drakma/doc/index.html 2011-02-05 07:45:59 UTC (rev 4641)
+++ trunk/thirdparty/drakma/doc/index.html 2011-02-07 17:27:23 UTC (rev 4642)
@@ -731,7 +731,55 @@
<!-- Entry for HTTP-REQUEST -->
-<p><br><table border=0><tr><td colspan=4 valign=top>[Function]</td></tr><tr><td valign=top style="white-space:nowrap"><a class=none name='http-request'><b>http-request</b></a> </td><td valign=top><i><a class=none href="#uri">uri</a> </i></td><td valign=top><tt>&key</tt> </td><td><i><a class=none href="#protocol">protocol</a> <a class=none href="#method">method</a> <a class=none href="#force-ssl">force-ssl</a> <a class=none href="#parameters">parameters</a> <a class=none href="#form-data">form-data</a> <a class=none href="#content">content</a> <a class=none href="#content-length">content-length</a> <a class=none href="#content-type">content-type</a> <a class=none href="#cookie-jar-param">cookie-jar</a> <a class=none href="#basic-authorization">basic-authorization</a> <a class=none href="#user-agent">user-agent</a> <a class=none href="#accept">accept</a> <a class=none href="#proxy">proxy</a> <a class=none href="#proxy-basic-authorization">proxy-basic-authorization</a> <a class=none href="#additional-headers">additional-headers</a> <a class=none href="#redirect">redirect</a> <a class=none href="#redirect-methods">redirect-methods</a> <a class=none href="#auto-referer">auto-referer</a> <a class=none href="#keep-alive">keep-alive</a> <a class=none href="#close">close</a> <a class=none href="#external-format-out">external-format-out</a> <a class=none href="#external-format-in">external-format-in</a> <a class=none href="#force-binary">force-binary</a> <a class=none href="#want-stream">want-stream</a> <a class=none href="#stream">stream</a> <a class=none href="#connection-timeout">connection-timeout</a> <a class=none href="#read-timeout">read-timeout</a> <a class=none href="#write-timeout">write-timeout</a> <a class=none href="#deadline">deadline</a></i></td></tr><tr><td colspan=2></td><td colspan=2 valign=top> => <i>body-or-stream, status-code, headers, uri, stream, <a class=none href="#must-close">must-close</a>, reason-phrase</i></td></tr></table>
+<p>
+ <br>
+ <table border=0>
+ <tr>
+ <td colspan=4 valign=top>[Function]</td>
+ </tr>
+ <tr>
+ <td valign=top style="white-space:nowrap"><a class=none name='http-request'><b>http-request</b></a> </td>
+ <td valign=top><i><a class=none href="#uri">uri</a> </i></td>
+ <td valign=top><tt>&key</tt> </td>
+ <td>
+ <i>
+ <a class=none href="#protocol">protocol</a>
+ <a class=none href="#method">method</a>
+ <a class=none href="#force-ssl">force-ssl</a>
+ <a class=none href="#parameters">parameters</a>
+ <a class=none href="#form-data">form-data</a>
+ <a class=none href="#content">content</a>
+ <a class=none href="#content-length">content-length</a>
+ <a class=none href="#content-type">content-type</a>
+ <a class=none href="#cookie-jar-param">cookie-jar</a>
+ <a class=none href="#basic-authorization">basic-authorization</a>
+ <a class=none href="#user-agent">user-agent</a>
+ <a class=none href="#accept">accept</a>
+ <a class=none href="#range">range</a>
+ <a class=none href="#proxy">proxy</a>
+ <a class=none href="#proxy-basic-authorization">proxy-basic-authorization</a>
+ <a class=none href="#additional-headers">additional-headers</a>
+ <a class=none href="#redirect">redirect</a>
+ <a class=none href="#redirect-methods">redirect-methods</a>
+ <a class=none href="#auto-referer">auto-referer</a>
+ <a class=none href="#keep-alive">keep-alive</a>
+ <a class=none href="#close">close</a>
+ <a class=none href="#external-format-out">external-format-out</a>
+ <a class=none href="#external-format-in">external-format-in</a>
+ <a class=none href="#force-binary">force-binary</a>
+ <a class=none href="#want-stream">want-stream</a>
+ <a class=none href="#stream">stream</a>
+ <a class=none href="#connection-timeout">connection-timeout</a>
+ <a class=none href="#read-timeout">read-timeout</a>
+ <a class=none href="#write-timeout">write-timeout</a>
+ <a class=none href="#deadline">deadline</a></i>
+ </td>
+ </tr>
+ <tr>
+ <td colspan=2></td>
+ <td colspan=2 valign=top> => <i>body-or-stream, status-code, headers, uri, stream, <a class=none href="#must-close">must-close</a>, reason-phrase</i></td>
+ </tr>
+ </table>
<blockquote><br>
Sends an <a href="http://www.rfc.net/rfc2616.html">HTTP</a> request to a web server and returns its reply.
@@ -935,6 +983,10 @@
it can be a string which is used
directly. <a class=none name="accept"><code><i>accept</i></code></a>, if not <code>NIL</code>, is the
'Accept' header sent - the default is <code>"*/*"</code>.
+<a class=none name="range"><code><i>range</i></code></a> optionally
+specifies a subrange of the resource to be requested. It must be
+specified as list of two integers which indicate the start and
+(inclusive) end offset of the requested range, in bytes
<p>
If <a class=none name="proxy"><code><i>proxy</i></code></a> is not <code>NIL</code>, it should be a
string denoting
Modified: trunk/thirdparty/drakma/request.lisp
===================================================================
--- trunk/thirdparty/drakma/request.lisp 2011-02-05 07:45:59 UTC (rev 4641)
+++ trunk/thirdparty/drakma/request.lisp 2011-02-07 17:27:23 UTC (rev 4642)
@@ -190,6 +190,7 @@
basic-authorization
(user-agent :drakma)
(accept "*/*")
+ range
proxy
proxy-basic-authorization
additional-headers
@@ -314,9 +315,15 @@
which denote the current version of Drakma or, in the latter four
cases, a fixed string corresponding to a more or less recent \(as
of August 2006) version of the corresponding browser. Or it can
-be a string which is used directly. ACCEPT, if not NIL, is the
-`Accept' header sent.
+be a string which is used directly.
+ACCEPT, if not NIL, specifies the contents of the `Accept' header
+sent.
+
+RANGE optionally specifies a subrange of the resource to be requested.
+It must be specified as list of two integers which indicate the start
+and (inclusive) end offset of the requested range, in bytes.
+
If PROXY is not NIL, it should be a string denoting a proxy
server through which the request should be sent. Or it can be a
list of two values - a string denoting the proxy server and an
@@ -404,6 +411,12 @@
(parameter-error "CLOSE and KEEP-ALIVE must not be both true."))
(when (and form-data (not (eq method :post)))
(parameter-error "FORM-DATA makes only sense with POST requests."))
+ (when range
+ (unless (and (listp range)
+ (integerp (first range))
+ (integerp (second range))
+ (<= (first range) (second range)))
+ (parameter-error "RANGE parameter must be specified as list of two integers, with the second larger or equal to the first")))
;; convert PROXY argument to canonical form
(when proxy
(when (atom proxy)
@@ -558,6 +571,8 @@
(second proxy-basic-authorization)))))
(when accept
(write-header "Accept" "~A" accept))
+ (when range
+ (write-header "Range" "bytes ~A-~A" (first range) (second range)))
(when cookie-jar
;; write all cookies in one fell swoop, so even Sun's
;; web server has a chance to get it