From 0c7731360347a9b789551fdb28ab8941d0d5c636 Mon Sep 17 00:00:00 2001 From: Filipe Brandenburger Date: Mon, 21 Sep 2026 13:52:57 -0700 Subject: [PATCH] Show the full URL in download error messages Downloads set req.URL.Opaque to keep "+" percent-encoded in the path. URL.String() prints "scheme:opaque" and drops the host, so every error from the http client showed a URL such as "https:/repos/debian/...", lacking a host name. The request itself was correct, but the message sent me to look for a malformed URL. Set req.URL.RawPath instead. The escaping still reaches the wire through EscapedPath(), and String() keeps the scheme and the host. The proxy special case is no longer necessary. Requests through a proxy now get the same "+" escaping as direct requests. --- http/download.go | 9 ++++----- http/download_test.go | 10 ++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/http/download.go b/http/download.go index 02b9275e4..dd656aacd 100644 --- a/http/download.go +++ b/http/download.go @@ -163,11 +163,10 @@ func (downloader *downloaderImpl) newRequest(ctx context.Context, method, url st req.Close = true req = req.WithContext(ctx) - proxyURL, _ := downloader.client.Transport.(*http.Transport).Proxy(req) - if proxyURL == nil && (req.URL.Scheme == "http" || req.URL.Scheme == "https") { - req.URL.Opaque = strings.Replace(req.URL.RequestURI(), "+", "%2b", -1) - req.URL.RawQuery = "" - } + // Some servers (Amazon S3) read a literal "+" in a path as a space, so keep + // it percent-encoded. RawPath does this without hiding the host the way + // URL.Opaque does, so error messages still show the full URL. + req.URL.RawPath = strings.ReplaceAll(req.URL.EscapedPath(), "+", "%2b") return req, nil } diff --git a/http/download_test.go b/http/download_test.go index 57ba78452..f35e009dc 100644 --- a/http/download_test.go +++ b/http/download_test.go @@ -109,6 +109,16 @@ func (s *DownloaderSuite) TestDownloadWithChecksum(c *C) { c.Check(checksums.SHA512, Equals, "bac18bf4e564856369acc2ed57300fecba3a2c1af5ae8304021e4252488678feb18118466382ee4e1210fe1f065080210e453a80cfb37ccb8752af3269df160e") } +func (s *DownloaderSuite) TestNewRequestEscapesPlus(c *C) { + d := s.d.(*downloaderImpl) + + req, err := d.newRequest(s.ctx, "GET", s.url+"/test/pkg_1.0+dfsg-1_amd64.deb?a=b") + c.Assert(err, IsNil) + c.Check(req.URL.RequestURI(), Equals, "/test/pkg_1.0%2bdfsg-1_amd64.deb?a=b") + // the host must stay visible, it ends up in error messages + c.Check(req.URL.String(), Equals, s.url+"/test/pkg_1.0%2bdfsg-1_amd64.deb?a=b") +} + func (s *DownloaderSuite) TestDownload404(c *C) { c.Assert(s.d.Download(s.ctx, s.url+"/doesntexist", s.tempfile.Name()), ErrorMatches, "HTTP code 404.*")