go-aptproxy/cache/downloader.go

99 lines
2.0 KiB
Go
Raw Normal View History

2016-05-01 06:00:43 +02:00
package cache
import (
"io"
"net/http"
"os"
"strconv"
"sync"
)
// DownloadError conveys information about a download request that failed.
type DownloadError struct {
Status string
}
// Error returns a description of the error.
func (d *DownloadError) Error() string {
return d.Status
}
2016-05-01 06:00:43 +02:00
// downloader attempts to download a file from a remote URL.
type downloader struct {
doneMutex sync.Mutex
err error
entry *Entry
entryMutex sync.Mutex
}
2016-05-01 06:00:43 +02:00
// newDownloader creates a new downloader.
func newDownloader(rawurl, jsonFilename, dataFilename string) *downloader {
d := &downloader{}
d.doneMutex.Lock()
d.entryMutex.Lock()
go func() {
defer func() {
d.doneMutex.Unlock()
}()
once := &sync.Once{}
trigger := func() {
once.Do(func() {
d.entryMutex.Unlock()
})
}
defer trigger()
resp, err := http.Get(rawurl)
if err != nil {
d.err = err
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
d.err = &DownloadError{
Status: resp.Status,
}
return
}
f, err := os.Create(dataFilename)
if err != nil {
d.err = err
return
}
defer f.Close()
d.entry = &Entry{
URL: rawurl,
ContentLength: strconv.FormatInt(resp.ContentLength, 10),
ContentType: resp.Header.Get("Content-Type"),
LastModified: resp.Header.Get("Last-Modified"),
}
if err = d.entry.Save(jsonFilename); err != nil {
d.err = err
return
}
trigger()
n, err := io.Copy(f, resp.Body)
if err != nil {
d.err = err
return
}
d.entry.ContentLength = strconv.FormatInt(n, 10)
d.entry.Complete = true
d.entry.Save(jsonFilename)
}()
return d
}
2016-05-01 06:00:43 +02:00
// GetEntry retrieves the entry associated with the download.
func (d *downloader) GetEntry() (*Entry, error) {
d.entryMutex.Lock()
defer d.entryMutex.Unlock()
2016-05-01 06:00:43 +02:00
return d.entry, d.err
}
2016-05-01 06:00:43 +02:00
// WaitForDone will block until the download completes.
func (d *downloader) WaitForDone() error {
d.doneMutex.Lock()
defer d.doneMutex.Unlock()
return d.err
}