summaryrefslogtreecommitdiff
path: root/module/web
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2015-03-01 23:41:01 +0100
committerAndy Wingo <wingo@pobox.com>2016-05-22 18:43:28 +0200
commit751a55e3552547c84cc3cc0ad69fc6f26bd7251e (patch)
tree9c8447ea2649b47ea2e19c04a274fcf28495d268 /module/web
parentc6d88d12345a3722bf78b124c69f15d73da23dc0 (diff)
downloadguile-751a55e3552547c84cc3cc0ad69fc6f26bd7251e.tar.gz
http: Do not buffer HTTP chunks.
Fixes <http://bugs.gnu.org/19939>. * module/web/http.scm (read-chunk, read-chunk-body): Remove. (make-chunked-input-port)[next-chunk, buffer-, buffer-size, buffer-pointer]: Remove. [chunk-size, remaining]: New variables. [read!]: Rewrite to write directly to BV. * test-suite/tests/web-http.test ("chunked encoding")["reads chunks without buffering", "reads across chunk boundaries"]: New tests.
Diffstat (limited to 'module/web')
-rw-r--r--module/web/http.scm66
1 files changed, 32 insertions, 34 deletions
diff --git a/module/web/http.scm b/module/web/http.scm
index 9e8e4a3a5..8093ed21d 100644
--- a/module/web/http.scm
+++ b/module/web/http.scm
@@ -1,6 +1,6 @@
;;; HTTP messages
-;; Copyright (C) 2010, 2011, 2012, 2013, 2014, 2016 Free Software Foundation, Inc.
+;; Copyright (C) 2010-2016 Free Software Foundation, Inc.
;; This library is free software; you can redistribute it and/or
;; modify it under the terms of the GNU Lesser General Public
@@ -1914,6 +1914,7 @@ treated specially, and is just returned as a plain string."
;; Chunked Responses
(define (read-chunk-header port)
+ "Read a chunk header and return the chunk size."
(let* ((str (read-line port))
(extension-start (string-index str (lambda (c) (or (char=? c #\;)
(char=? c #\return)))))
@@ -1923,53 +1924,50 @@ treated specially, and is just returned as a plain string."
16)))
size))
-(define (read-chunk port)
- (let ((size (read-chunk-header port)))
- (read-chunk-body port size)))
-
-(define (read-chunk-body port size)
- (let ((bv (get-bytevector-n port size)))
- (get-u8 port) ; CR
- (get-u8 port) ; LF
- bv))
-
(define* (make-chunked-input-port port #:key (keep-alive? #f))
"Returns a new port which translates HTTP chunked transfer encoded
data from PORT into a non-encoded format. Returns eof when it has
read the final chunk from PORT. This does not necessarily mean
that there is no more data on PORT. When the returned port is
closed it will also close PORT, unless the KEEP-ALIVE? is true."
- (define (next-chunk)
- (read-chunk port))
- (define finished? #f)
(define (close)
(unless keep-alive?
(close-port port)))
- (define buffer #vu8())
- (define buffer-size 0)
- (define buffer-pointer 0)
+
+ (define chunk-size 0) ;size of the current chunk
+ (define remaining 0) ;number of bytes left from the current chunk
+ (define finished? #f) ;did we get all the chunks?
+
(define (read! bv idx to-read)
(define (loop to-read num-read)
(cond ((or finished? (zero? to-read))
num-read)
- ((<= to-read (- buffer-size buffer-pointer))
- (bytevector-copy! buffer buffer-pointer
- bv (+ idx num-read)
- to-read)
- (set! buffer-pointer (+ buffer-pointer to-read))
- (loop 0 (+ num-read to-read)))
- (else
- (let ((n (- buffer-size buffer-pointer)))
- (bytevector-copy! buffer buffer-pointer
- bv (+ idx num-read)
- n)
- (set! buffer (next-chunk))
- (set! buffer-pointer 0)
- (set! buffer-size (bytevector-length buffer))
- (set! finished? (= buffer-size 0))
- (loop (- to-read n)
- (+ num-read n))))))
+ ((zero? remaining) ;get a new chunk
+ (let ((size (read-chunk-header port)))
+ (set! chunk-size size)
+ (set! remaining size)
+ (if (zero? size)
+ (begin
+ (set! finished? #t)
+ num-read)
+ (loop to-read num-read))))
+ (else ;read from the current chunk
+ (let* ((ask-for (min to-read remaining))
+ (read (get-bytevector-n! port bv (+ idx num-read)
+ ask-for)))
+ (if (eof-object? read)
+ (begin ;premature termination
+ (set! finished? #t)
+ num-read)
+ (let ((left (- remaining read)))
+ (set! remaining left)
+ (when (zero? left)
+ ;; We're done with this chunk; read CR and LF.
+ (get-u8 port) (get-u8 port))
+ (loop (- to-read read)
+ (+ num-read read))))))))
(loop to-read 0))
+
(make-custom-binary-input-port "chunked input port" read! #f #f close))
(define* (make-chunked-output-port port #:key (keep-alive? #f)