go-aptproxy/cache/downloader.go
2016-04-30 21:00:43 -07:00

88 lines
1.7 KiB
Go

package cache
import (
"errors"
"io"
"net/http"
"os"
"strconv"
"sync"
)
// downloader attempts to download a file from a remote URL.
type downloader struct {
doneMutex sync.Mutex
err error
entry *Entry
entryMutex sync.Mutex
}
// 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 = errors.New(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
}
// GetEntry retrieves the entry associated with the download.
func (d *downloader) GetEntry() (*Entry, error) {
d.entryMutex.Lock()
defer d.entryMutex.Unlock()
return d.entry, d.err
}
// WaitForDone will block until the download completes.
func (d *downloader) WaitForDone() error {
d.doneMutex.Lock()
defer d.doneMutex.Unlock()
return d.err
}