go-aptproxy/downloader.go
2016-04-26 13:12:46 -07:00

89 lines
1.8 KiB
Go

package main
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 waits until the Entry associated with the download is available.
// This call will block until the entry is available or an error occurs.
func (d *Downloader) GetEntry() *Entry {
d.entryMutex.Lock()
defer d.entryMutex.Unlock()
return d.entry
}
// Wait will block until the download completes.
func (d *Downloader) Wait() error {
d.doneMutex.Lock()
defer d.doneMutex.Unlock()
return d.err
}