summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2013-05-15 23:40:09 +0200
committerLudovic Courtès <ludo@gnu.org>2013-05-15 23:40:09 +0200
commit101d9f3fd43b436d5dc7ef13e644c7fbbc7f62d5 (patch)
treee3a4eed4c2e50b932d333fa5344d343125c42cfd
parent3d6b71e87eca505262f9756644d72e545c7e48f8 (diff)
substitute-binary: Pass `filtered-port' an unbuffered port.
This fixes a bug whereby `read-response' would read more than just the response, with the extra data going into the port's buffer; the "bzip2 -dc" process spawned by `filtered-port' would not see the those buffered data, which are definitely lost, and would bail out with "bzip2: (stdin) is not a bzip2 file." * guix/utils.scm (filtered-port): Document that INPUT must be unbuffered. * guix/web.scm (http-fetch): Add `buffered?' parameter. Call `open-socket-for-uri' explicitly, and call `setvbuf' when BUFFERED? is false. Pass the port to `http-get'. Close it upon 301/302. * guix/scripts/substitute-binary.scm (fetch): Add `buffered?' parameter. Pass it to `http-fetch'; honor it for `file' URIs. (guix-substitute-binary): Call `fetch' with #:buffered? #f for port RAW. * tests/utils.scm ("filtered-port, file"): Open FILE as unbuffered.
-rwxr-xr-xguix/scripts/substitute-binary.scm8
-rw-r--r--guix/utils.scm3
-rw-r--r--guix/web.scm23
-rw-r--r--tests/utils.scm21
4 files changed, 34 insertions, 21 deletions
diff --git a/guix/scripts/substitute-binary.scm b/guix/scripts/substitute-binary.scm
index 27a43b9e3fc..1317a72fb1a 100755
--- a/guix/scripts/substitute-binary.scm
+++ b/guix/scripts/substitute-binary.scm
@@ -117,15 +117,17 @@ pairs."
117 (else 117 (else
118 (error "unmatched line" line))))) 118 (error "unmatched line" line)))))
119 119
120(define (fetch uri) 120(define* (fetch uri #:key (buffered? #t))
121 "Return a binary input port to URI and the number of bytes it's expected to 121 "Return a binary input port to URI and the number of bytes it's expected to
122provide." 122provide."
123 (case (uri-scheme uri) 123 (case (uri-scheme uri)
124 ((file) 124 ((file)
125 (let ((port (open-input-file (uri-path uri)))) 125 (let ((port (open-input-file (uri-path uri))))
126 (unless buffered?
127 (setvbuf port _IONBF))
126 (values port (stat:size (stat port))))) 128 (values port (stat:size (stat port)))))
127 ((http) 129 ((http)
128 (http-fetch uri #:text? #f)))) 130 (http-fetch uri #:text? #f #:buffered? buffered?))))
129 131
130(define-record-type <cache> 132(define-record-type <cache>
131 (%make-cache url store-directory wants-mass-query?) 133 (%make-cache url store-directory wants-mass-query?)
@@ -423,7 +425,7 @@ indefinitely."
423 (format #t "~a~%" (narinfo-hash narinfo)) 425 (format #t "~a~%" (narinfo-hash narinfo))
424 426
425 (let*-values (((raw download-size) 427 (let*-values (((raw download-size)
426 (fetch uri)) 428 (fetch uri #:buffered? #f))
427 ((input pids) 429 ((input pids)
428 (decompressed-port (narinfo-compression narinfo) 430 (decompressed-port (narinfo-compression narinfo)
429 raw))) 431 raw)))
diff --git a/guix/utils.scm b/guix/utils.scm
index c2d2808f766..25a392e6a88 100644
--- a/guix/utils.scm
+++ b/guix/utils.scm
@@ -163,7 +163,8 @@ evaluate to a simple datum."
163(define (filtered-port command input) 163(define (filtered-port command input)
164 "Return an input port where data drained from INPUT is filtered through 164 "Return an input port where data drained from INPUT is filtered through
165COMMAND (a list). In addition, return a list of PIDs that the caller must 165COMMAND (a list). In addition, return a list of PIDs that the caller must
166wait." 166wait. When INPUT is a file port, it must be unbuffered; otherwise, any
167buffered data is lost."
167 (let loop ((input input) 168 (let loop ((input input)
168 (pids '())) 169 (pids '()))
169 (if (file-port? input) 170 (if (file-port? input)
diff --git a/guix/web.scm b/guix/web.scm
index 2236bfd6216..e9c69cb0c05 100644
--- a/guix/web.scm
+++ b/guix/web.scm
@@ -141,20 +141,30 @@ closed it will also close PORT, unless the KEEP-ALIVE? is true."
141(module-define! (resolve-module '(web client)) 141(module-define! (resolve-module '(web client))
142 'shutdown (const #f)) 142 'shutdown (const #f))
143 143
144(define* (http-fetch uri #:key (text? #f)) 144(define* (http-fetch uri #:key (text? #f) (buffered? #t))
145 "Return an input port containing the data at URI, and the expected number of 145 "Return an input port containing the data at URI, and the expected number of
146bytes available or #f. If TEXT? is true, the data at URI is considered to be 146bytes available or #f. If TEXT? is true, the data at URI is considered to be
147textual. Follow any HTTP redirection." 147textual. Follow any HTTP redirection. When BUFFERED? is #f, return an
148unbuffered port, suitable for use in `filtered-port'."
148 (let loop ((uri uri)) 149 (let loop ((uri uri))
150 (define port
151 (let ((s (open-socket-for-uri uri)))
152 (unless buffered?
153 (setvbuf s _IONBF))
154 s))
155
149 (let*-values (((resp data) 156 (let*-values (((resp data)
150 ;; Try hard to use the API du jour to get an input port. 157 ;; Try hard to use the API du jour to get an input port.
151 ;; On Guile 2.0.5 and before, we can only get a string or 158 ;; On Guile 2.0.5 and before, we can only get a string or
152 ;; bytevector, and not an input port. Work around that. 159 ;; bytevector, and not an input port. Work around that.
153 (if (version>? "2.0.7" (version)) 160 (if (version>? "2.0.7" (version))
154 (if (defined? 'http-get*) 161 (if (defined? 'http-get*)
155 (http-get* uri #:decode-body? text?) ; 2.0.7 162 (http-get* uri #:decode-body? text?
156 (http-get uri #:decode-body? text?)) ; 2.0.5- 163 #:port port) ; 2.0.7
157 (http-get uri #:streaming? #t))) ; 2.0.9+ 164 (http-get uri #:decode-body? text?
165 #:port port)) ; 2.0.5-
166 (http-get uri #:streaming? #t
167 #:port port))) ; 2.0.9+
158 ((code) 168 ((code)
159 (response-code resp))) 169 (response-code resp)))
160 (case code 170 (case code
@@ -182,7 +192,8 @@ textual. Follow any HTTP redirection."
182 ((301 ; moved permanently 192 ((301 ; moved permanently
183 302) ; found (redirection) 193 302) ; found (redirection)
184 (let ((uri (response-location resp))) 194 (let ((uri (response-location resp)))
185 (format #t "following redirection to `~a'...~%" 195 (close-port port)
196 (format #t (_ "following redirection to `~a'...~%")
186 (uri->string uri)) 197 (uri->string uri))
187 (loop uri))) 198 (loop uri)))
188 (else 199 (else
diff --git a/tests/utils.scm b/tests/utils.scm
index c2fb2741930..e8549204d07 100644
--- a/tests/utils.scm
+++ b/tests/utils.scm
@@ -102,17 +102,16 @@
102 list)) 102 list))
103 103
104(test-assert "filtered-port, file" 104(test-assert "filtered-port, file"
105 (let ((file (search-path %load-path "guix.scm"))) 105 (let* ((file (search-path %load-path "guix.scm"))
106 (call-with-input-file file 106 (input (open-file file "r0")))
107 (lambda (input) 107 (let*-values (((compressed pids1)
108 (let*-values (((compressed pids1) 108 (filtered-port `(,%gzip "-c" "--fast") input))
109 (filtered-port `(,%gzip "-c" "--fast") input)) 109 ((decompressed pids2)
110 ((decompressed pids2) 110 (filtered-port `(,%gzip "-d") compressed)))
111 (filtered-port `(,%gzip "-d") compressed))) 111 (and (every (compose zero? cdr waitpid)
112 (and (every (compose zero? cdr waitpid) 112 (append pids1 pids2))
113 (append pids1 pids2)) 113 (equal? (get-bytevector-all decompressed)
114 (equal? (get-bytevector-all decompressed) 114 (call-with-input-file file get-bytevector-all))))))
115 (call-with-input-file file get-bytevector-all))))))))
116 115
117(test-assert "filtered-port, non-file" 116(test-assert "filtered-port, non-file"
118 (let ((data (call-with-input-file (search-path %load-path "guix.scm") 117 (let ((data (call-with-input-file (search-path %load-path "guix.scm")