New upstream version 1.5.0+ds1

This commit is contained in:
Roland Mas
2023-01-02 14:19:29 +01:00
parent 29e4ea6ec0
commit 5c4f97f88e
324 changed files with 15360 additions and 6668 deletions
+2 -2
View File
@@ -39,7 +39,7 @@ var compressionMethods = []struct {
// DownloadTryCompression tries to download from URL .bz2, .gz and raw extension until
// it finds existing file.
func DownloadTryCompression(ctx context.Context, downloader aptly.Downloader, baseURL *url.URL, path string, expectedChecksums map[string]utils.ChecksumInfo, ignoreMismatch bool, maxTries int) (io.Reader, *os.File, error) {
func DownloadTryCompression(ctx context.Context, downloader aptly.Downloader, baseURL *url.URL, path string, expectedChecksums map[string]utils.ChecksumInfo, ignoreMismatch bool) (io.Reader, *os.File, error) {
var err error
for _, method := range compressionMethods {
@@ -63,7 +63,7 @@ func DownloadTryCompression(ctx context.Context, downloader aptly.Downloader, ba
if foundChecksum {
expected := expectedChecksums[bestSuffix]
file, err = DownloadTempWithChecksum(ctx, downloader, tryURL.String(), &expected, ignoreMismatch, maxTries)
file, err = DownloadTempWithChecksum(ctx, downloader, tryURL.String(), &expected, ignoreMismatch)
} else {
if !ignoreMismatch {
continue
+9 -9
View File
@@ -44,7 +44,7 @@ func (s *CompressionSuite) TestDownloadTryCompression(c *C) {
buf = make([]byte, 4)
d := NewFakeDownloader()
d.ExpectResponse("http://example.com/file.bz2", bzipData)
r, file, err := DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false, 1)
r, file, err := DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false)
c.Assert(err, IsNil)
defer file.Close()
io.ReadFull(r, buf)
@@ -56,7 +56,7 @@ func (s *CompressionSuite) TestDownloadTryCompression(c *C) {
d = NewFakeDownloader()
d.ExpectError("http://example.com/file.bz2", &Error{Code: 404})
d.ExpectResponse("http://example.com/file.gz", gzipData)
r, file, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false, 1)
r, file, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false)
c.Assert(err, IsNil)
defer file.Close()
io.ReadFull(r, buf)
@@ -69,7 +69,7 @@ func (s *CompressionSuite) TestDownloadTryCompression(c *C) {
d.ExpectError("http://example.com/file.bz2", &Error{Code: 404})
d.ExpectError("http://example.com/file.gz", &Error{Code: 404})
d.ExpectResponse("http://example.com/file.xz", xzData)
r, file, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false, 1)
r, file, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false)
c.Assert(err, IsNil)
defer file.Close()
io.ReadFull(r, buf)
@@ -83,7 +83,7 @@ func (s *CompressionSuite) TestDownloadTryCompression(c *C) {
d.ExpectError("http://example.com/file.gz", &Error{Code: 404})
d.ExpectError("http://example.com/file.xz", &Error{Code: 404})
d.ExpectResponse("http://example.com/file", rawData)
r, file, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false, 1)
r, file, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false)
c.Assert(err, IsNil)
defer file.Close()
io.ReadFull(r, buf)
@@ -94,7 +94,7 @@ func (s *CompressionSuite) TestDownloadTryCompression(c *C) {
d = NewFakeDownloader()
d.ExpectError("http://example.com/file.bz2", &Error{Code: 404})
d.ExpectResponse("http://example.com/file.gz", "x")
_, _, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", nil, true, 1)
_, _, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", nil, true)
c.Assert(err, ErrorMatches, "unexpected EOF")
c.Assert(d.Empty(), Equals, true)
}
@@ -112,7 +112,7 @@ func (s *CompressionSuite) TestDownloadTryCompressionLongestSuffix(c *C) {
buf = make([]byte, 4)
d := NewFakeDownloader()
d.ExpectResponse("http://example.com/subdir/file.bz2", bzipData)
r, file, err := DownloadTryCompression(s.ctx, d, s.baseURL, "subdir/file", expectedChecksums, false, 1)
r, file, err := DownloadTryCompression(s.ctx, d, s.baseURL, "subdir/file", expectedChecksums, false)
c.Assert(err, IsNil)
defer file.Close()
io.ReadFull(r, buf)
@@ -122,7 +122,7 @@ func (s *CompressionSuite) TestDownloadTryCompressionLongestSuffix(c *C) {
func (s *CompressionSuite) TestDownloadTryCompressionErrors(c *C) {
d := NewFakeDownloader()
_, _, err := DownloadTryCompression(s.ctx, d, s.baseURL, "file", nil, true, 1)
_, _, err := DownloadTryCompression(s.ctx, d, s.baseURL, "file", nil, true)
c.Assert(err, ErrorMatches, "unexpected request.*")
d = NewFakeDownloader()
@@ -130,7 +130,7 @@ func (s *CompressionSuite) TestDownloadTryCompressionErrors(c *C) {
d.ExpectError("http://example.com/file.gz", &Error{Code: 404})
d.ExpectError("http://example.com/file.xz", &Error{Code: 404})
d.ExpectError("http://example.com/file", errors.New("403"))
_, _, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", nil, true, 1)
_, _, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", nil, true)
c.Assert(err, ErrorMatches, "403")
d = NewFakeDownloader()
@@ -144,6 +144,6 @@ func (s *CompressionSuite) TestDownloadTryCompressionErrors(c *C) {
"file.xz": {Size: 7},
"file": {Size: 7},
}
_, _, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false, 1)
_, _, err = DownloadTryCompression(s.ctx, d, s.baseURL, "file", expectedChecksums, false)
c.Assert(err, ErrorMatches, "checksums don't match.*")
}
+98 -11
View File
@@ -4,8 +4,10 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
@@ -28,12 +30,13 @@ var (
type downloaderImpl struct {
progress aptly.Progress
aggWriter io.Writer
maxTries int
client *http.Client
}
// NewDownloader creates new instance of Downloader which specified number
// of threads and download limit in bytes/sec
func NewDownloader(downLimit int64, progress aptly.Progress) aptly.Downloader {
func NewDownloader(downLimit int64, maxTries int, progress aptly.Progress) aptly.Downloader {
transport := http.Transport{}
transport.Proxy = http.DefaultTransport.(*http.Transport).Proxy
transport.ResponseHeaderTimeout = 30 * time.Second
@@ -45,20 +48,35 @@ func NewDownloader(downLimit int64, progress aptly.Progress) aptly.Downloader {
downloader := &downloaderImpl{
progress: progress,
maxTries: maxTries,
client: &http.Client{
Transport: &transport,
},
}
progressWriter := io.Writer(progress)
if progress == nil {
progressWriter = ioutil.Discard
}
downloader.client.CheckRedirect = downloader.checkRedirect
if downLimit > 0 {
downloader.aggWriter = flowrate.NewWriter(progress, downLimit)
downloader.aggWriter = flowrate.NewWriter(progressWriter, downLimit)
} else {
downloader.aggWriter = progress
downloader.aggWriter = progressWriter
}
return downloader
}
func (downloader *downloaderImpl) checkRedirect(req *http.Request, via []*http.Request) error {
if downloader.progress != nil {
downloader.progress.Printf("Following redirect to %s...\n", req.URL)
}
return nil
}
// GetProgress returns Progress object
func (downloader *downloaderImpl) GetProgress() aptly.Progress {
return downloader.progress
@@ -71,7 +89,19 @@ func (downloader *downloaderImpl) GetLength(ctx context.Context, url string) (in
return -1, err
}
resp, err := downloader.client.Do(req)
var resp *http.Response
maxTries := downloader.maxTries
for maxTries > 0 {
resp, err = downloader.client.Do(req)
if err != nil && retryableError(err) {
maxTries--
} else {
// stop retrying
break
}
}
if err != nil {
return -1, errors.Wrap(err, url)
}
@@ -81,6 +111,10 @@ func (downloader *downloaderImpl) GetLength(ctx context.Context, url string) (in
}
if resp.ContentLength < 0 {
// an existing, but zero-length file can be reported with ContentLength -1
if resp.StatusCode == 200 && resp.ContentLength == -1 {
return 0, nil
}
return -1, fmt.Errorf("could not determine length of %s", url)
}
@@ -89,10 +123,25 @@ func (downloader *downloaderImpl) GetLength(ctx context.Context, url string) (in
// Download starts new download task
func (downloader *downloaderImpl) Download(ctx context.Context, url string, destination string) error {
return downloader.DownloadWithChecksum(ctx, url, destination, nil, false, 1)
return downloader.DownloadWithChecksum(ctx, url, destination, nil, false)
}
func retryableError(err error) bool {
// unwrap errors.Wrap
err = errors.Cause(err)
// unwrap *url.Error
if wrapped, ok := err.(*url.Error); ok {
err = wrapped.Err
}
switch err {
case io.EOF:
return true
case io.ErrUnexpectedEOF:
return true
}
switch err.(type) {
case *net.OpError:
return true
@@ -101,11 +150,13 @@ func retryableError(err error) bool {
case net.Error:
return true
}
return false
// Note: make all errors retryable
return true
}
func (downloader *downloaderImpl) newRequest(ctx context.Context, method, url string) (*http.Request, error) {
req, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, errors.Wrap(err, url)
}
@@ -123,27 +174,57 @@ func (downloader *downloaderImpl) newRequest(ctx context.Context, method, url st
// DownloadWithChecksum starts new download task with checksum verification
func (downloader *downloaderImpl) DownloadWithChecksum(ctx context.Context, url string, destination string,
expected *utils.ChecksumInfo, ignoreMismatch bool, maxTries int) error {
expected *utils.ChecksumInfo, ignoreMismatch bool) error {
if downloader.progress != nil {
downloader.progress.Printf("Downloading %s...\n", url)
defer downloader.progress.Flush()
}
req, err := downloader.newRequest(ctx, "GET", url)
var temppath string
maxTries := downloader.maxTries
const delayMax = time.Duration(5 * time.Minute)
delay := time.Duration(1 * time.Second)
const delayMultiplier = 2
for maxTries > 0 {
temppath, err = downloader.download(req, url, destination, expected, ignoreMismatch)
if err != nil && retryableError(err) {
maxTries--
if err != nil {
if retryableError(err) {
if downloader.progress != nil {
downloader.progress.Printf("Error downloading %s: %s retrying...\n", url, err)
}
maxTries--
time.Sleep(delay)
// Sleep exponentially at the next retry, but no longer than delayMax
delay *= delayMultiplier
if delay > delayMax {
delay = delayMax
}
} else {
if downloader.progress != nil {
downloader.progress.Printf("Error downloading %s: %s cannot retry...\n", url, err)
}
break
}
} else {
// get out of the loop
if downloader.progress != nil {
downloader.progress.Printf("Success downloading %s\n", url)
}
break
}
if downloader.progress != nil {
downloader.progress.Printf("Retrying %d %s...\n", maxTries, url)
}
}
// still an error after retrying, giving up
if err != nil {
if downloader.progress != nil {
downloader.progress.Printf("Giving up on %s...\n", url)
}
return err
}
@@ -183,7 +264,11 @@ func (downloader *downloaderImpl) download(req *http.Request, url, destination s
defer outfile.Close()
checksummer := utils.NewChecksumWriter()
writers := []io.Writer{outfile, downloader.aggWriter}
writers := []io.Writer{outfile}
if downloader.progress != nil {
writers = append(writers, downloader.progress)
}
if expected != nil {
writers = append(writers, checksummer)
@@ -214,7 +299,9 @@ func (downloader *downloaderImpl) download(req *http.Request, url, destination s
if err != nil {
if ignoreMismatch {
downloader.progress.Printf("WARNING: %s\n", err.Error())
if downloader.progress != nil {
downloader.progress.Printf("WARNING: %s\n", err.Error())
}
} else {
os.Remove(temppath)
return "", err
+1
View File
@@ -1,3 +1,4 @@
//go:build go1.7
// +build go1.7
package http
+25 -12
View File
@@ -7,6 +7,7 @@ import (
"net"
"net/http"
"os"
"os/user"
"github.com/aptly-dev/aptly/aptly"
"github.com/aptly-dev/aptly/console"
@@ -45,7 +46,7 @@ func (s *DownloaderSuiteBase) SetUpTest(c *C) {
s.progress = console.NewProgress()
s.progress.Start()
s.d = NewDownloader(0, s.progress)
s.d = NewDownloader(0, 1, s.progress)
s.ctx = context.Background()
}
@@ -78,32 +79,32 @@ func (s *DownloaderSuite) TestDownloadOK(c *C) {
}
func (s *DownloaderSuite) TestDownloadWithChecksum(c *C) {
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{}, false, 1),
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{}, false),
ErrorMatches, ".*size check mismatch 12 != 0")
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "abcdef"}, false, 1),
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "abcdef"}, false),
ErrorMatches, ".*md5 hash mismatch \"a1acb0fe91c7db45ec4d775192ec5738\" != \"abcdef\"")
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "abcdef"}, true, 1),
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "abcdef"}, true),
IsNil)
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738"}, false, 1),
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738"}, false),
IsNil)
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738", SHA1: "abcdef"}, false, 1),
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738", SHA1: "abcdef"}, false),
ErrorMatches, ".*sha1 hash mismatch \"921893bae6ad6fd818401875d6779254ef0ff0ec\" != \"abcdef\"")
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738",
SHA1: "921893bae6ad6fd818401875d6779254ef0ff0ec"}, false, 1),
SHA1: "921893bae6ad6fd818401875d6779254ef0ff0ec"}, false),
IsNil)
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738",
SHA1: "921893bae6ad6fd818401875d6779254ef0ff0ec", SHA256: "abcdef"}, false, 1),
SHA1: "921893bae6ad6fd818401875d6779254ef0ff0ec", SHA256: "abcdef"}, false),
ErrorMatches, ".*sha256 hash mismatch \"b3c92ee1246176ed35f6e8463cd49074f29442f5bbffc3f8591cde1dcc849dac\" != \"abcdef\"")
checksums := utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738",
SHA1: "921893bae6ad6fd818401875d6779254ef0ff0ec", SHA256: "b3c92ee1246176ed35f6e8463cd49074f29442f5bbffc3f8591cde1dcc849dac"}
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &checksums, false, 1),
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &checksums, false),
IsNil)
// download backfills missing checksums
c.Check(checksums.SHA512, Equals, "bac18bf4e564856369acc2ed57300fecba3a2c1af5ae8304021e4252488678feb18118466382ee4e1210fe1f065080210e453a80cfb37ccb8752af3269df160e")
@@ -115,13 +116,25 @@ func (s *DownloaderSuite) TestDownload404(c *C) {
}
func (s *DownloaderSuite) TestDownloadConnectError(c *C) {
c.Assert(s.d.Download(s.ctx, "http://nosuch.localhost/", s.tempfile.Name()),
c.Assert(s.d.Download(s.ctx, "http://nosuch.host/", s.tempfile.Name()),
ErrorMatches, ".*no such host")
}
func skipIfRoot(c *C) {
currentUser, err := user.Current()
if err != nil {
c.Skip("Unknown user")
}
if currentUser.Username == "root" {
c.Skip("Root user")
}
}
func (s *DownloaderSuite) TestDownloadFileError(c *C) {
skipIfRoot(c)
c.Assert(s.d.Download(s.ctx, s.url+"/test", "/"),
ErrorMatches, ".*permission denied")
ErrorMatches, ".*(permission denied|read-only file system)")
}
func (s *DownloaderSuite) TestGetLength(c *C) {
@@ -138,7 +151,7 @@ func (s *DownloaderSuite) TestGetLength404(c *C) {
}
func (s *DownloaderSuite) TestGetLengthConnectError(c *C) {
_, err := s.d.GetLength(s.ctx, "http://nosuch.localhost/")
_, err := s.d.GetLength(s.ctx, "http://nosuch.host/")
c.Assert(err, ErrorMatches, ".*no such host")
}
+2 -2
View File
@@ -89,7 +89,7 @@ func (f *FakeDownloader) getExpectedRequest(url string) (*expectedRequest, error
}
// DownloadWithChecksum performs fake download by matching against first expectation in the queue or any expectation, with cheksum verification
func (f *FakeDownloader) DownloadWithChecksum(ctx context.Context, url string, filename string, expected *utils.ChecksumInfo, ignoreMismatch bool, maxTries int) error {
func (f *FakeDownloader) DownloadWithChecksum(ctx context.Context, url string, filename string, expected *utils.ChecksumInfo, ignoreMismatch bool) error {
expectation, err := f.getExpectedRequest(url)
if err != nil {
return err
@@ -130,7 +130,7 @@ func (f *FakeDownloader) DownloadWithChecksum(ctx context.Context, url string, f
// Download performs fake download by matching against first expectation in the queue
func (f *FakeDownloader) Download(ctx context.Context, url string, filename string) error {
return f.DownloadWithChecksum(ctx, url, filename, nil, false, 1)
return f.DownloadWithChecksum(ctx, url, filename, nil, false)
}
// GetProgress returns Progress object
+173
View File
@@ -0,0 +1,173 @@
package http
import (
"context"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"net/http"
"time"
"golang.org/x/time/rate"
"github.com/aptly-dev/aptly/utils"
"github.com/cavaliergopher/grab/v3"
"github.com/pkg/errors"
"github.com/aptly-dev/aptly/aptly"
)
type GrabDownloader struct {
client *grab.Client
progress aptly.Progress
maxTries int
downLimit int64
}
// Check interface
var (
_ aptly.Downloader = (*GrabDownloader)(nil)
)
// NewGrabDownloader creates new expected downloader
func NewGrabDownloader(downLimit int64, maxTries int, progress aptly.Progress) *GrabDownloader {
client := grab.NewClient()
return &GrabDownloader{
client: client,
progress: progress,
maxTries: maxTries,
downLimit: downLimit,
}
}
func (d *GrabDownloader) Download(ctx context.Context, url string, destination string) error {
return d.DownloadWithChecksum(ctx, url, destination, nil, false)
}
func (d *GrabDownloader) DownloadWithChecksum(ctx context.Context, url string, destination string, expected *utils.ChecksumInfo, ignoreMismatch bool) error {
maxTries := d.maxTries
const delayMax = time.Duration(5 * time.Minute)
delay := time.Duration(1 * time.Second)
const delayMultiplier = 2
err := fmt.Errorf("No tries available")
for maxTries > 0 {
err = d.download(ctx, url, destination, expected, ignoreMismatch)
if err == nil {
// Success
break
}
d.log("Error downloading %s: %v\n", url, err)
if retryableError(err) {
maxTries--
d.log("Retrying download %s: %d\n", url, maxTries)
time.Sleep(delay)
} else {
// Can't retry
d.log("Cannot retry download %s\n", url)
break
}
}
return err
}
func (d *GrabDownloader) log(msg string, a ...interface{}) {
fmt.Printf(msg, a...)
if d.progress != nil {
d.progress.Printf(msg, a...)
}
}
func (d *GrabDownloader) maybeSetupChecksum(req *grab.Request, expected *utils.ChecksumInfo) error {
if expected == nil {
// Nothing to setup
return nil
}
if expected.MD5 != "" {
expectedHash, err := hex.DecodeString(expected.MD5)
if err != nil {
return err
}
req.SetChecksum(md5.New(), expectedHash, true)
} else if expected.SHA1 != "" {
expectedHash, err := hex.DecodeString(expected.SHA1)
if err != nil {
return err
}
req.SetChecksum(sha1.New(), expectedHash, true)
} else if expected.SHA256 != "" {
expectedHash, err := hex.DecodeString(expected.SHA256)
if err != nil {
return err
}
req.SetChecksum(sha256.New(), expectedHash, true)
} else if expected.SHA512 != "" {
expectedHash, err := hex.DecodeString(expected.SHA512)
if err != nil {
return err
}
req.SetChecksum(sha512.New(), expectedHash, true)
}
req.Size = expected.Size
return nil
}
func (d *GrabDownloader) download(ctx context.Context, url string, destination string, expected *utils.ChecksumInfo, ignoreMismatch bool) error {
// TODO clean up dest dir on permanent failure
d.log("Download %s -> %s\n", url, destination)
req, err := grab.NewRequest(destination, url)
if err != nil {
d.log("Error creating new request: %v\n", err)
return errors.Wrap(err, url)
}
if d.downLimit > 0 {
req.RateLimiter = rate.NewLimiter(rate.Limit(d.downLimit), int(d.downLimit))
}
d.maybeSetupChecksum(req, expected)
if err != nil {
d.log("Error setting up checksum: %v\n", err)
return errors.Wrap(err, url)
}
resp := d.client.Do(req)
Loop:
for {
select {
case <-resp.Done:
// download is complete
break Loop
}
}
err = resp.Err()
if err != nil && err == grab.ErrBadChecksum && ignoreMismatch {
fmt.Printf("Ignoring checksum mismatch for %s\n", url)
return nil
}
return err
}
func (d *GrabDownloader) GetProgress() aptly.Progress {
return d.progress
}
func (d *GrabDownloader) GetLength(ctx context.Context, url string) (int64, error) {
resp, err := http.Head(url)
if err != nil {
return -1, err
}
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return -1, &Error{Code: resp.StatusCode, URL: url}
}
if resp.ContentLength < 0 {
return -1, fmt.Errorf("could not determine length of %s", url)
}
return resp.ContentLength, nil
}
+137
View File
@@ -0,0 +1,137 @@
package http
import (
"context"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"github.com/aptly-dev/aptly/aptly"
"github.com/aptly-dev/aptly/console"
"github.com/aptly-dev/aptly/utils"
. "gopkg.in/check.v1"
)
type GrabDownloaderSuiteBase struct {
tempfile *os.File
l net.Listener
url string
ch chan struct{}
progress aptly.Progress
d aptly.Downloader
ctx context.Context
}
func (s *GrabDownloaderSuiteBase) SetUpTest(c *C) {
s.tempfile, _ = ioutil.TempFile(os.TempDir(), "aptly-test")
s.l, _ = net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)})
s.url = fmt.Sprintf("http://localhost:%d", s.l.Addr().(*net.TCPAddr).Port)
mux := http.NewServeMux()
mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s", r.URL.Path)
})
s.ch = make(chan struct{})
go func() {
http.Serve(s.l, mux)
close(s.ch)
}()
s.progress = console.NewProgress()
s.progress.Start()
s.d = NewGrabDownloader(0, 1, s.progress)
s.ctx = context.Background()
}
func (s *GrabDownloaderSuiteBase) TearDownTest(c *C) {
s.progress.Shutdown()
s.l.Close()
<-s.ch
os.Remove(s.tempfile.Name())
s.tempfile.Close()
}
type GrabDownloaderSuite struct {
GrabDownloaderSuiteBase
}
var _ = Suite(&GrabDownloaderSuite{})
func (s *GrabDownloaderSuite) SetUpTest(c *C) {
s.GrabDownloaderSuiteBase.SetUpTest(c)
}
func (s *GrabDownloaderSuite) TearDownTest(c *C) {
s.GrabDownloaderSuiteBase.TearDownTest(c)
}
func (s *GrabDownloaderSuite) TestDownloadOK(c *C) {
c.Assert(s.d.Download(s.ctx, s.url+"/test", s.tempfile.Name()), IsNil)
}
func (s *GrabDownloaderSuite) TestDownloadWithChecksum(c *C) {
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 1}, false),
// ErrorMatches, ".*size check mismatch 12 != 1")
ErrorMatches, "bad content length")
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "abcdef"}, false),
ErrorMatches, "checksum mismatch")
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "abcdef"}, true),
IsNil)
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738"}, false),
IsNil)
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738",
SHA1: "921893bae6ad6fd818401875d6779254ef0ff0ec"}, false),
IsNil)
checksums := utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738",
SHA1: "921893bae6ad6fd818401875d6779254ef0ff0ec", SHA256: "b3c92ee1246176ed35f6e8463cd49074f29442f5bbffc3f8591cde1dcc849dac"}
c.Assert(s.d.DownloadWithChecksum(s.ctx, s.url+"/test", s.tempfile.Name(), &checksums, false),
IsNil)
}
func (s *GrabDownloaderSuite) TestDownload404(c *C) {
c.Assert(s.d.Download(s.ctx, s.url+"/doesntexist", s.tempfile.Name()),
ErrorMatches, ".* 404 .*")
}
func (s *GrabDownloaderSuite) TestDownloadConnectError(c *C) {
c.Assert(s.d.Download(s.ctx, "http://nosuch.host/", s.tempfile.Name()),
ErrorMatches, ".*no such host")
}
func (s *GrabDownloaderSuite) TestDownloadFileError(c *C) {
skipIfRoot(c)
c.Assert(s.d.Download(s.ctx, s.url+"/test", "/"),
ErrorMatches, ".*(permission denied|read-only file system)")
}
func (s *GrabDownloaderSuite) TestGetLength(c *C) {
size, err := s.d.GetLength(s.ctx, s.url+"/test")
c.Assert(err, IsNil)
c.Assert(size, Equals, int64(12))
}
func (s *GrabDownloaderSuite) TestGetLength404(c *C) {
_, err := s.d.GetLength(s.ctx, s.url+"/doesntexist")
c.Assert(err, ErrorMatches, "HTTP code 404.*")
}
func (s *GrabDownloaderSuite) TestGetLengthConnectError(c *C) {
_, err := s.d.GetLength(s.ctx, "http://nosuch.host/")
c.Assert(err, ErrorMatches, ".*no such host")
}
+4 -4
View File
@@ -14,13 +14,13 @@ import (
//
// Temporary file would be already removed, so no need to cleanup
func DownloadTemp(ctx context.Context, downloader aptly.Downloader, url string) (*os.File, error) {
return DownloadTempWithChecksum(ctx, downloader, url, nil, false, 1)
return DownloadTempWithChecksum(ctx, downloader, url, nil, false)
}
// DownloadTempWithChecksum is a DownloadTemp with checksum verification
//
// Temporary file would be already removed, so no need to cleanup
func DownloadTempWithChecksum(ctx context.Context, downloader aptly.Downloader, url string, expected *utils.ChecksumInfo, ignoreMismatch bool, maxTries int) (*os.File, error) {
func DownloadTempWithChecksum(ctx context.Context, downloader aptly.Downloader, url string, expected *utils.ChecksumInfo, ignoreMismatch bool) (*os.File, error) {
tempdir, err := ioutil.TempDir(os.TempDir(), "aptly")
if err != nil {
return nil, err
@@ -30,11 +30,11 @@ func DownloadTempWithChecksum(ctx context.Context, downloader aptly.Downloader,
tempfile := filepath.Join(tempdir, "buffer")
if expected != nil && downloader.GetProgress() != nil {
downloader.GetProgress().InitBar(expected.Size, true)
downloader.GetProgress().InitBar(expected.Size, true, aptly.BarMirrorUpdateDownloadIndexes)
defer downloader.GetProgress().ShutdownBar()
}
err = downloader.DownloadWithChecksum(ctx, url, tempfile, expected, ignoreMismatch, maxTries)
err = downloader.DownloadWithChecksum(ctx, url, tempfile, expected, ignoreMismatch)
if err != nil {
return nil, err
}
+2 -2
View File
@@ -38,12 +38,12 @@ func (s *TempSuite) TestDownloadTemp(c *C) {
func (s *TempSuite) TestDownloadTempWithChecksum(c *C) {
f, err := DownloadTempWithChecksum(s.ctx, s.d, s.url+"/test", &utils.ChecksumInfo{Size: 12, MD5: "a1acb0fe91c7db45ec4d775192ec5738",
SHA1: "921893bae6ad6fd818401875d6779254ef0ff0ec", SHA256: "b3c92ee1246176ed35f6e8463cd49074f29442f5bbffc3f8591cde1dcc849dac"}, false, 1)
SHA1: "921893bae6ad6fd818401875d6779254ef0ff0ec", SHA256: "b3c92ee1246176ed35f6e8463cd49074f29442f5bbffc3f8591cde1dcc849dac"}, false)
c.Assert(err, IsNil)
c.Assert(f.Close(), IsNil)
_, err = DownloadTempWithChecksum(s.ctx, s.d, s.url+"/test", &utils.ChecksumInfo{Size: 13}, false, 1)
_, err = DownloadTempWithChecksum(s.ctx, s.d, s.url+"/test", &utils.ChecksumInfo{Size: 13}, false)
c.Assert(err, ErrorMatches, ".*size check mismatch 12 != 13")
}