diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2013-05-15 23:40:09 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2013-05-15 23:40:09 +0200 |
| commit | 101d9f3fd43b436d5dc7ef13e644c7fbbc7f62d5 (patch) | |
| tree | e3a4eed4c2e50b932d333fa5344d343125c42cfd | |
| parent | 3d6b71e87eca505262f9756644d72e545c7e48f8 (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-x | guix/scripts/substitute-binary.scm | 8 | ||||
| -rw-r--r-- | guix/utils.scm | 3 | ||||
| -rw-r--r-- | guix/web.scm | 23 | ||||
| -rw-r--r-- | tests/utils.scm | 21 |
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 |
| 122 | provide." | 122 | provide." |
| 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 |
| 165 | COMMAND (a list). In addition, return a list of PIDs that the caller must | 165 | COMMAND (a list). In addition, return a list of PIDs that the caller must |
| 166 | wait." | 166 | wait. When INPUT is a file port, it must be unbuffered; otherwise, any |
| 167 | buffered 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 |
| 146 | bytes available or #f. If TEXT? is true, the data at URI is considered to be | 146 | bytes available or #f. If TEXT? is true, the data at URI is considered to be |
| 147 | textual. Follow any HTTP redirection." | 147 | textual. Follow any HTTP redirection. When BUFFERED? is #f, return an |
| 148 | unbuffered 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") |
