Dear drakma users,

I made drakma accept gzipped message bodies by default, and also
automatically decode them.  A patch is attached.

Unfortunately this adds a new dependency on gzip-sequence (which in
turn depends on salza2).  See the docstrings for more details.  To sum
it up: if WANT-STREAM is T, It wraps the returned stream in a
flexi-stream that wraps a gzip-input-stream.  Otherwise, it decodes
the sequence before returning from HTTP-REQUEST.

Thanks,
Red
Only in patched/: conditions.fasl
Only in patched/: cookies.fasl
Common subdirectories: drakma-1.1.0/doc and patched/doc
diff -u drakma-1.1.0/drakma.asd patched/drakma.asd
--- drakma-1.1.0/drakma.asd	2009-12-01 14:44:55.000000000 -0800
+++ patched/drakma.asd	2010-01-20 15:16:35.135381687 -0800
@@ -58,5 +58,6 @@
                :cl-base64
                :chunga
                :flexi-streams
+	       :gzip-stream
                #-:lispworks :usocket
                #-(or :lispworks :allegro) :cl+ssl))
Only in patched/: drakma.asd~
Only in patched/: packages.fasl
Only in patched/: read.fasl
Only in patched/: request.fasl
diff -u drakma-1.1.0/request.lisp patched/request.lisp
--- drakma-1.1.0/request.lisp	2009-12-01 14:36:55.000000000 -0800
+++ patched/request.lisp	2010-01-20 16:32:47.674544332 -0800
@@ -47,7 +47,7 @@
                                            '("chunked" "identity")
                                            :test #'equalp))
                      (null (set-difference content-encodings
-                                           '("identity")
+                                           *decodable-content-encodings*
                                            :test #'equalp)))
             (let* ((charset (parameter-value "charset" params))
                    (name (cond (charset (as-keyword charset))
@@ -131,15 +131,19 @@
       (format stream "--~A--" boundary)
       (crlf))))
 
-(defun read-body (stream headers must-close textp)
+(defun read-body (stream headers must-close textp decodep)
   "Reads the message body from the HTTP stream STREAM using the
 information contained in HEADERS \(as produced by HTTP-REQUEST).  If
-TEXTP is true, the body is assumed to be of content type `text' and
-will be returned as a string.  Otherwise an array of octets \(or NIL
-for an empty body) is returned.  Returns the optional `trailer' HTTP
-headers of the chunked stream \(if any) as a second value."
+DECODEP is true and a gzip `Content-Encoding' header is found, the
+body is gunzipped.  If TEXTP is true, the body is assumed to be of
+content type `text' and will be returned as a string.  Otherwise an
+array of octets \(or NIL for an empty body) is returned.  Returns the
+optional `trailer' HTTP headers of the chunked stream \(if any) as a
+second value."
   (let ((content-length (ignore-errors
                            (parse-integer (header-value :content-length headers))))
+	(gzip-encoded? (ignore-errors
+			 (string-equal "gzip" (header-value :content-encoding headers))))
         (element-type (if textp
                         #+:lispworks 'lw:simple-char #-:lispworks 'character
                         'octet))
@@ -154,6 +158,8 @@
                      #+:clisp
                      (setf (flexi-stream-element-type stream) 'octet)
                      (read-sequence result stream)
+		     (when (and decodep gzip-encoded?)
+		       (setf result (gzip-stream:gunzip-sequence result)))
                      (when textp
                        (setf result
                              (octets-to-string result :external-format (flexi-stream-external-format stream))
@@ -187,6 +193,8 @@
                               basic-authorization
                               (user-agent :drakma)
                               (accept "*/*")
+		              (accept-encoding "gzip")
+		              (decompress t)
                               proxy
                               proxy-basic-authorization
                               additional-headers
@@ -313,6 +321,15 @@
 be a string which is used directly.  ACCEPT, if not NIL, is the
 `Accept' header sent.
 
+ACCEPT-ENCODING denotes the value for the `Accept-Encoding' header.
+The default value is \"gzip\" and a null value prevents the header
+from being sent.  If DECOMPRESS is not NIL, and the resulting value
+has a \"gzip\" `Content-Encoding' header, then the content will
+automatically be decompressed: if the body is returned, it will be the
+decompressed body sequence or string; if WANT-STREAM is T, then
+BODY-OR-STREAM is a flexi-stream backed by a gzip-input-stream, which
+in turn wraps the socket stream.
+
 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
@@ -482,6 +499,7 @@
                         (cl+ssl:make-ssl-client-stream 
                          (cl+ssl:stream-fd s)
                          :close-callback (lambda () (close s)))))))
+	    
             (cond (stream
                    (setf (flexi-stream-element-type http-stream)
                          #+:lispworks 'lw:simple-char #-:lispworks 'character
@@ -532,6 +550,8 @@
                                        (second proxy-basic-authorization)))))
               (when accept
                 (write-header "Accept" "~A" accept))
+	      (when accept-encoding
+		(write-header "Accept-Encoding" "~A" accept-encoding))
               (when cookie-jar
                 ;; write all cookies in one fell swoop, so even Sun's
                 ;; web server has a chance to get it
@@ -669,30 +689,47 @@
                                  (when (member "chunked" transfer-encodings :test #'equalp)
                                    (setf (chunked-stream-input-chunking-p
                                           (flexi-stream-stream http-stream)) t)))
-                               (when (setq external-format-body
-                                           (and (not force-binary)
-                                                (funcall *body-format-function*
-                                                         headers external-format-in)))
-                                 (setf (flexi-stream-external-format http-stream)
-                                       external-format-body))
+                               (when (and
+				      (setq external-format-body
+					    (and (not force-binary)
+						 (let ((*decodable-content-encodings*
+							(if (not decompress)
+							    (remove "gzip" *decodable-content-encodings* :test #'equal)
+							    *decodable-content-encodings*)))
+						   (funcall *body-format-function*
+							    headers external-format-in))))
+				      (not (and decompress
+						(ignore-errors
+						  (string-equal "gzip" (header-value :content-encoding headers))))))
+				 (setf (flexi-stream-external-format http-stream)
+				       external-format-body))
                                (when force-binary
                                  (setf (flexi-stream-element-type http-stream) 'octet))
                                (unless (or want-stream (eq method :head))
                                  (let (trailers)
                                    (multiple-value-setq (body trailers)
-                                       (read-body http-stream headers must-close external-format-body))
+                                       (read-body http-stream headers must-close external-format-body decompress))
                                    (when trailers
                                      (drakma-warn "Adding trailers from chunked encoding to HTTP headers.")
                                      (setq headers (nconc headers trailers)))))
                                (setq done t)
-                               (values (cond (want-stream http-stream)
-                                             (t body))
-                                       status-code
-                                       headers
-                                       uri
-                                       http-stream
-                                       must-close
-                                       status-text))))))
+			       (let* ((client-stream
+				       (cond ((and want-stream decompress
+						   (ignore-errors
+						     (string-equal "gzip" (header-value :content-encoding headers))))
+					      (setf must-close t)
+					      (make-flexi-stream (gzip-stream:make-gzip-input-stream http-stream)
+								 :external-format external-format-body))
+					     (t http-stream)))
+				      (body-or-stream (cond (want-stream http-stream)
+							    (t body))))
+				 (values body-or-stream
+					 status-code
+					 headers
+					 uri
+					 client-stream
+					 must-close
+					 status-text)))))))
                 (when (eq content :continuation)
                   (return-from http-request #'finish-request))
                 (finish-request content))))
Only in patched/: request.lisp~
Only in patched/: specials.fasl
diff -u drakma-1.1.0/specials.lisp patched/specials.lisp
--- drakma-1.1.0/specials.lisp	2009-11-25 09:03:40.000000000 -0800
+++ patched/specials.lisp	2010-01-20 15:05:58.316225197 -0800
@@ -90,6 +90,12 @@
 STRING-EQUAL to the car or if the car is NIL and if the subtype part
 is STRING-EQUAL to the cdr or if the cdr is NIL.")
 
+(defvar *decodable-content-encodings* '("identity" "gzip")
+  "A list of values for the `Content-Encoding' header that Draka will
+decode before the body sequence is processed further in READ-BODY.
+This value may affect whether *BODY-FORMAT-FUNCTION* returns NIL or
+not.")
+
 (defvar *body-format-function* 'determine-body-format
   "A function which determines whether the content body returned by
 the server is text and should be treated as such or not.  The function
Only in patched/: specials.lisp~
Only in patched/: util.fasl
_______________________________________________
drakma-devel mailing list
drakma-devel@common-lisp.net
http://common-lisp.net/cgi-bin/mailman/listinfo/drakma-devel

Reply via email to