1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
15
"launchpad.net/juju-core/log"
18
// Status represents the status of a completed download.
20
// File holds the downloaded data on success.
22
// Err describes any error encountered while downloading.
26
// Download can download a file from the network.
27
type Download struct {
32
// New returns a new Download instance downloading from the given URL to
33
// the given directory. If dir is empty, it defaults to os.TempDir().
34
func New(url, dir string) *Download {
36
done: make(chan Status),
42
// Stop stops any download that's in progress.
43
func (d *Download) Stop() {
48
// Done returns a channel that receives a status when the download has
49
// completed. It is the receiver's responsibility to close and remove
51
func (d *Download) Done() <-chan Status {
55
func (d *Download) run(url, dir string) {
57
file, err := download(url, dir)
59
err = fmt.Errorf("cannot download %q: %v", url, err)
66
case d.done <- status:
67
case <-d.tomb.Dying():
68
cleanTempFile(status.File)
72
func download(url, dir string) (file *os.File, err error) {
76
tempFile, err := ioutil.TempFile(dir, "inprogress-")
82
cleanTempFile(tempFile)
85
// TODO(rog) make the download operation interruptible.
86
resp, err := http.Get(url)
90
defer resp.Body.Close()
91
if resp.StatusCode != http.StatusOK {
92
return nil, fmt.Errorf("bad http response: %v", resp.Status)
94
_, err = io.Copy(tempFile, resp.Body)
98
if _, err := tempFile.Seek(0, 0); err != nil {
104
func cleanTempFile(f *os.File) {
107
if err := os.Remove(f.Name()); err != nil {
108
log.Warningf("downloader: cannot remove temp file %q: %v", f.Name(), err)