aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark H Weaver <mhw@netris.org>2015-02-18 19:33:10 -0500
committerMark H Weaver <mhw@netris.org>2015-02-19 03:32:22 -0500
commit04dec194d8e460831ec0695a944d9c7313affea2 (patch)
tree7b384927264e072da1211aff2a9c58b0ac606be1
parente92a4ad928e869e98043f1f4afc7df20666bbf02 (diff)
downloadguix-04dec194d8e460831ec0695a944d9c7313affea2.tar
guix-04dec194d8e460831ec0695a944d9c7313affea2.tar.gz
download: Handle HTTP redirects to relative URI references.
Fixes <http://bugs.gnu.org/19840>. Reported by Ricardo Wurmus <rekado@elephly.net>. * guix/build/download.scm: On Guile 2.0.11 or earlier, redefine the http "Location" header to accept relative URIs. (resolve-uri-reference): New exported procedure. (http-fetch): Use 'resolve-uri-reference' to resolve redirections. * guix/http-client.scm (http-fetch): Use 'resolve-uri-reference'
-rw-r--r--guix/build/download.scm82
-rw-r--r--guix/http-client.scm4
2 files changed, 84 insertions, 2 deletions
diff --git a/guix/build/download.scm b/guix/build/download.scm
index 5928ccd154..16afb1dce1 100644
--- a/guix/build/download.scm
+++ b/guix/build/download.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -29,6 +30,7 @@
#:use-module (ice-9 match)
#:use-module (ice-9 format)
#:export (open-connection-for-uri
+ resolve-uri-reference
maybe-expand-mirrors
url-fetch
progress-proc
@@ -204,6 +206,84 @@ which is not available during bootstrap."
(module-define! (resolve-module '(web client))
'shutdown (const #f))
+;; XXX: Work around <http://bugs.gnu.org/19840>, present in Guile
+;; up to 2.0.11.
+(unless (or (> (string->number (major-version)) 2)
+ (> (string->number (minor-version)) 0)
+ (> (string->number (micro-version)) 11))
+ (let ((declare-relative-uri-header!
+ (module-ref (resolve-module '(web http))
+ 'declare-relative-uri-header!)))
+ (declare-relative-uri-header! "Location")))
+
+(define (resolve-uri-reference ref base)
+ "Resolve the URI reference REF, interpreted relative to the BASE URI, into a
+target URI, according to the algorithm specified in RFC 3986 section 5.2.2.
+Return the resulting target URI."
+
+ (define (merge-paths base-path rel-path)
+ (let* ((base-components (string-split base-path #\/))
+ (base-directory-components (match base-components
+ ((components ... last) components)
+ (() '())))
+ (base-directory (string-join base-directory-components "/")))
+ (string-append base-directory "/" rel-path)))
+
+ (define (remove-dot-segments path)
+ (let loop ((in
+ ;; Drop leading "." and ".." components from a relative path.
+ ;; (absolute paths will start with a "" component)
+ (drop-while (match-lambda
+ ((or "." "..") #t)
+ (_ #f))
+ (string-split path #\/)))
+ (out '()))
+ (match in
+ (("." . rest)
+ (loop rest out))
+ ((".." . rest)
+ (match out
+ ((or () (""))
+ (error "remove-dot-segments: too many '..' components" path))
+ (_
+ (loop rest (cdr out)))))
+ ((component . rest)
+ (loop rest (cons component out)))
+ (()
+ (string-join (reverse out) "/")))))
+
+ (cond ((or (uri-scheme ref)
+ (uri-host ref))
+ (build-uri (or (uri-scheme ref)
+ (uri-scheme base))
+ #:userinfo (uri-userinfo ref)
+ #:host (uri-host ref)
+ #:port (uri-port ref)
+ #:path (remove-dot-segments (uri-path ref))
+ #:query (uri-query ref)
+ #:fragment (uri-fragment ref)))
+ ((string-null? (uri-path ref))
+ (build-uri (uri-scheme base)
+ #:userinfo (uri-userinfo base)
+ #:host (uri-host base)
+ #:port (uri-port base)
+ #:path (remove-dot-segments (uri-path base))
+ #:query (or (uri-query ref)
+ (uri-query base))
+ #:fragment (uri-fragment ref)))
+ (else
+ (build-uri (uri-scheme base)
+ #:userinfo (uri-userinfo base)
+ #:host (uri-host base)
+ #:port (uri-port base)
+ #:path (remove-dot-segments
+ (if (string-prefix? "/" (uri-path ref))
+ (uri-path ref)
+ (merge-paths (uri-path base)
+ (uri-path ref))))
+ #:query (uri-query ref)
+ #:fragment (uri-fragment ref)))))
+
(define (http-fetch uri file)
"Fetch data from URI and write it to FILE. Return FILE on success."
@@ -260,7 +340,7 @@ which is not available during bootstrap."
file))
((301 ; moved permanently
302) ; found (redirection)
- (let ((uri (response-location resp)))
+ (let ((uri (resolve-uri-reference (response-location resp) uri)))
(format #t "following redirection to `~a'...~%"
(uri->string uri))
(close connection)
diff --git a/guix/http-client.scm b/guix/http-client.scm
index 4770628e45..aad7656e19 100644
--- a/guix/http-client.scm
+++ b/guix/http-client.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2012 Free Software Foundation, Inc.
;;;
;;; This file is part of GNU Guix.
@@ -29,6 +30,7 @@
#:use-module (rnrs bytevectors)
#:use-module (guix ui)
#:use-module (guix utils)
+ #:use-module ((guix build download) #:select (resolve-uri-reference))
#:export (&http-get-error
http-get-error?
http-get-error-uri
@@ -227,7 +229,7 @@ Raise an '&http-get-error' condition if downloading fails."
(values data len)))))
((301 ; moved permanently
302) ; found (redirection)
- (let ((uri (response-location resp)))
+ (let ((uri (resolve-uri-reference (response-location resp) uri)))
(close-port port)
(format #t (_ "following redirection to `~a'...~%")
(uri->string uri))