~ chicken-core (chicken-5) d464fe9396435af8c8fdc6744a01359fdd647a1b
commit d464fe9396435af8c8fdc6744a01359fdd647a1b
Author: Peter Bex <peter@more-magic.net>
AuthorDate: Sun Sep 15 16:17:35 2019 +0200
Commit: Evan Hanson <evhan@foldling.org>
CommitDate: Sun Sep 29 11:47:11 2019 +1300
Handle 301/302 redirects in chicken-install
This was noted as a TODO, now implemented.
Signed-off-by: Evan Hanson <evhan@foldling.org>
diff --git a/NEWS b/NEWS
index 9b71bd35..84a65d48 100644
--- a/NEWS
+++ b/NEWS
@@ -44,6 +44,7 @@
been emitted as a separate file (for example with "-J").
- chicken-install now correctly checks server response code to avoid
interpreting error response bodies (like 404, 500) as Scheme code.
+ - chicken-install now follows HTTP redirects when downloading eggs.
5.1.0
diff --git a/egg-download.scm b/egg-download.scm
index 15dddbbd..1acc09a6 100644
--- a/egg-download.scm
+++ b/egg-download.scm
@@ -27,6 +27,7 @@
(define +default-tcp-connect-timeout+ 30000) ; 30 seconds
(define +default-tcp-read/write-timeout+ 30000) ; 30 seconds
(define +url-regex+ "(http://)?([^/:]+)(:([^:/]+))?(/.*)?")
+(define +max-redirects+ 3)
(tcp-connect-timeout +default-tcp-connect-timeout+)
(tcp-read-timeout +default-tcp-read/write-timeout+)
@@ -60,15 +61,25 @@
(http-retrieve-response in len)))
(define (http-connect host port locn proxy-host proxy-port proxy-user-pass)
- (d "connecting to host ~s, port ~a ~a...~%" host port
- (if proxy-host
- (sprintf "(via ~a:~a) " proxy-host proxy-port)
- ""))
- (let-values (((in out)
+ (let next-req ((redirects 0)
+ (host host)
+ (port port)
+ (locn locn)
+ (req (make-HTTP-GET/1.1
+ locn user-agent host
+ port: port accept: "*/*"
+ proxy-host: proxy-host proxy-port: proxy-port)))
+
+ (when (= redirects +max-redirects+)
+ (network-failure "too many redirects" redirects))
+
+ (d "connecting to host ~s, port ~a ~a...~%" host port
+ (if proxy-host
+ (sprintf "(via ~a:~a) " proxy-host proxy-port)
+ ""))
+
+ (let-values (((in out)
(tcp-connect (or proxy-host host) (or proxy-port port))))
- (let next-req ((req (make-HTTP-GET/1.1 locn user-agent host
- port: port accept: "*/*"
- proxy-host: proxy-host proxy-port: proxy-port)))
(d "requesting ~s ...~%" locn)
(display req out)
(flush-output out)
@@ -77,34 +88,58 @@
(datalen #f)
(h1 (read-line in))
(response-match (match-http-response h1)))
- (d "~a~%" h1)
- ;;XXX handle redirects here
- (cond
+
+ (define (process-headers)
+ (let ((ln (read-line in)))
+ (unless (equal? ln "")
+ (cond ((match-chunked-transfer-encoding ln)
+ (set! chunked #t))
+ ((match-content-length ln) =>
+ (lambda (sz) (set! datalen sz)))
+ ((match-location ln) =>
+ (lambda (new-locn)
+ (set!-values (host port locn)
+ (deconstruct-url new-locn)))))
+ (d "~a~%" ln)
+ (process-headers) ) ) )
+
+ (d "~a~%" h1)
+
+ (cond
((response-match-code? response-match 407)
- (let-values (((inpx outpx) (tcp-connect proxy-host proxy-port)))
- (set! in inpx) (set! out outpx)
- (next-req (make-HTTP-GET/1.1
- locn user-agent host port: port
- accept: "*/*"
- proxy-host: proxy-host proxy-port: proxy-port
- proxy-user-pass: proxy-user-pass))))
+ (close-input-port in)
+ (close-output-port out)
+
+ (d "retrying with proxy auth ~a~%" locn)
+ (next-req redirects host port locn
+ (make-HTTP-GET/1.1
+ locn user-agent host port: port
+ accept: "*/*"
+ proxy-host: proxy-host proxy-port: proxy-port
+ proxy-user-pass: proxy-user-pass)))
+
+ ((or (response-match-code? response-match 301)
+ (response-match-code? response-match 302))
+ (process-headers)
+ (close-input-port in)
+ (close-output-port out)
+
+ (d "redirected to ~a~%" locn)
+ (next-req (add1 redirects) host port locn
+ (make-HTTP-GET/1.1
+ locn user-agent host
+ port: port accept: "*/*"
+ proxy-host: proxy-host proxy-port: proxy-port)))
+
((response-match-code? response-match 200)
- (let loop ()
- (let ((ln (read-line in)))
- (unless (equal? ln "")
- (cond ((match-chunked-transfer-encoding ln)
- (set! chunked #t))
- ((match-content-length ln) =>
- (lambda (sz) (set! datalen sz))))
- (d "~a~%" ln)
- (loop) ) ) )
+ (process-headers)
(when chunked
(d "reading chunks ")
(let ((data (read-chunks in)))
(close-input-port in)
- (set! in (open-input-string data))) ))
- (else (network-failure "invalid response from server" h1)))
- (values in out datalen)))))
+ (set! in (open-input-string data))) )
+ (values in out datalen))
+ (else (network-failure "invalid response from server" h1)))))))
(define (http-retrieve-files in out dest)
(d "reading files ...~%")
@@ -196,6 +231,10 @@
(define (match-chunked-transfer-encoding ln)
(irregex-match "[Tt]ransfer-[Ee]ncoding:\\s*chunked.*" ln) )
+(define (match-location ln)
+ (let ((m (irregex-match "[Ll]ocation:\\s*(.+)\\s*" ln)))
+ (and m (irregex-match-substring m 1))))
+
(define (match-content-length ln)
(let ((m (irregex-match "[Cc]ontent-[Ll]ength:\\s*([0-9]+).*" ln)))
(and m (string->number (irregex-match-substring m 1)))))
Trap