summary refs log tree commit diff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2014-03-01 15:38:11 +0100
committerLudovic Courtès <ludo@gnu.org>2014-03-01 15:38:11 +0100
commit706e9e575d136299ef7d2623842c7a47dfbc6e27 (patch)
tree25786020a60337577acdc24aec060a37f0279620
parent1f7fd80032ef74015bb9a731e7c9a0a6d5d41f42 (diff)
downloadguix-706e9e575d136299ef7d2623842c7a47dfbc6e27.tar.gz
substitute-binary: Gracefully handle HTTP GET errors.
* guix/http-client.scm (&http-get-error): New condition type.
  (http-fetch): Raise it instead of using 'error'.
* guix/scripts/substitute-binary.scm (fetch) <http>: Wrap body into
  'guard' form; gracefully handle 'http-get-error?' conditions.
-rw-r--r--guix/http-client.scm35
-rwxr-xr-xguix/scripts/substitute-binary.scm60
2 files changed, 62 insertions, 33 deletions
diff --git a/guix/http-client.scm b/guix/http-client.scm
index 11231cbc1e..1f05df4b05 100644
--- a/guix/http-client.scm
+++ b/guix/http-client.scm
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org>
 ;;; Copyright © 2012 Free Software Foundation, Inc.
 ;;;
 ;;; This file is part of GNU Guix.
@@ -23,19 +23,36 @@
   #:use-module (web client)
   #:use-module (web response)
   #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-34)
+  #:use-module (srfi srfi-35)
   #:use-module (rnrs io ports)
   #:use-module (rnrs bytevectors)
   #:use-module (guix ui)
   #:use-module (guix utils)
-  #:export (open-socket-for-uri
+  #:export (&http-get-error
+            http-get-error?
+            http-get-error-uri
+            http-get-error-code
+            http-get-error-reason
+
+            open-socket-for-uri
             http-fetch))
 
 ;;; Commentary:
 ;;;
-;;; HTTP client portable among Guile versions.
+;;; HTTP client portable among Guile versions, and with proper error condition
+;;; reporting.
 ;;;
 ;;; Code:
 
+;; HTTP GET error.
+(define-condition-type &http-get-error &error
+  http-get-error?
+  (uri    http-get-error-uri)                     ; URI
+  (code   http-get-error-code)                    ; integer
+  (reason http-get-error-reason))                 ; string
+
+
 (define-syntax when-guile<=2.0.5
   (lambda (s)
     (syntax-case s ()
@@ -154,7 +171,9 @@ unbuffered."
   "Return an input port containing the data at URI, and the expected number of
 bytes available or #f.  If TEXT? is true, the data at URI is considered to be
 textual.  Follow any HTTP redirection.  When BUFFERED? is #f, return an
-unbuffered port, suitable for use in `filtered-port'."
+unbuffered port, suitable for use in `filtered-port'.
+
+Raise an '&http-get-error' condition if downloading fails."
   (let loop ((uri uri))
     (let ((port (or port
                     (open-socket-for-uri uri
@@ -202,7 +221,11 @@ unbuffered port, suitable for use in `filtered-port'."
                      (uri->string uri))
              (loop uri)))
           (else
-           (error "download failed" uri code
-                  (response-reason-phrase resp))))))))
+           (raise (condition (&http-get-error
+                              (uri uri)
+                              (code code)
+                              (reason (response-reason-phrase resp)))
+                             (&message
+                              (message "download failed"))))))))))
 
 ;;; http-client.scm ends here
diff --git a/guix/scripts/substitute-binary.scm b/guix/scripts/substitute-binary.scm
index 3aaa1c4284..54f4aaa6c0 100755
--- a/guix/scripts/substitute-binary.scm
+++ b/guix/scripts/substitute-binary.scm
@@ -38,6 +38,7 @@
   #:use-module (srfi srfi-11)
   #:use-module (srfi srfi-19)
   #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-34)
   #:use-module (web uri)
   #:use-module (guix http-client)
   #:export (guix-substitute-binary))
@@ -133,33 +134,38 @@ provide."
                             (if buffered? "rb" "r0b"))))
        (values port (stat:size (stat port)))))
     ((http)
-     ;; On Guile 2.0.5, `http-fetch' fetches the whole thing at once.  So
-     ;; honor TIMEOUT? to disable the timeout when fetching a nar.
-     ;;
-     ;; Test this with:
-     ;;   sudo tc qdisc add dev eth0 root netem delay 1500ms
-     ;; and then cancel with:
-     ;;   sudo tc qdisc del dev eth0 root
-     (let ((port #f))
-       (with-timeout (if (or timeout? (guile-version>? "2.0.5"))
-                         %fetch-timeout
-                         0)
-         (begin
-           (warning (_ "while fetching ~a: server is unresponsive~%")
-                    (uri->string uri))
-           (warning (_ "try `--no-substitutes' if the problem persists~%"))
-
-           ;; Before Guile v2.0.9-39-gfe51c7b, EINTR was reported to the user,
-           ;; and thus PORT had to be closed and re-opened.  This is not the
-           ;; case afterward.
-           (unless (or (guile-version>? "2.0.9")
-                       (version>? (version) "2.0.9.39"))
-             (when port
-               (close-port port))))
-         (begin
-           (when (or (not port) (port-closed? port))
-             (set! port (open-socket-for-uri uri #:buffered? buffered?)))
-           (http-fetch uri #:text? #f #:port port)))))))
+     (guard (c ((http-get-error? c)
+                (leave (_ "download from '~a' failed: ~a, ~s~%")
+                       (uri->string (http-get-error-uri c))
+                       (http-get-error-code c)
+                       (http-get-error-reason c))))
+       ;; On Guile 2.0.5, `http-fetch' fetches the whole thing at once.  So
+       ;; honor TIMEOUT? to disable the timeout when fetching a nar.
+       ;;
+       ;; Test this with:
+       ;;   sudo tc qdisc add dev eth0 root netem delay 1500ms
+       ;; and then cancel with:
+       ;;   sudo tc qdisc del dev eth0 root
+       (let ((port #f))
+         (with-timeout (if (or timeout? (guile-version>? "2.0.5"))
+                           %fetch-timeout
+                           0)
+           (begin
+             (warning (_ "while fetching ~a: server is unresponsive~%")
+                      (uri->string uri))
+             (warning (_ "try `--no-substitutes' if the problem persists~%"))
+
+             ;; Before Guile v2.0.9-39-gfe51c7b, EINTR was reported to the user,
+             ;; and thus PORT had to be closed and re-opened.  This is not the
+             ;; case afterward.
+             (unless (or (guile-version>? "2.0.9")
+                         (version>? (version) "2.0.9.39"))
+               (when port
+                 (close-port port))))
+           (begin
+             (when (or (not port) (port-closed? port))
+               (set! port (open-socket-for-uri uri #:buffered? buffered?)))
+             (http-fetch uri #:text? #f #:port port))))))))
 
 (define-record-type <cache>
   (%make-cache url store-directory wants-mass-query?)