~rogpeppe/juju-core/azure

« back to all changes in this revision

Viewing changes to downloader/downloader.go

  • Committer: Gustavo Niemeyer
  • Date: 2011-12-20 18:59:17 UTC
  • mto: This revision was merged to the branch mainline in revision 34.
  • Revision ID: gustavo@niemeyer.net-20111220185917-erfh4gkuk5w2gsu1
store: use log package

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Copyright 2012, 2013 Canonical Ltd.
2
 
// Licensed under the AGPLv3, see LICENCE file for details.
3
 
 
4
 
package downloader
5
 
 
6
 
import (
7
 
        "fmt"
8
 
        "io"
9
 
        "io/ioutil"
10
 
        "net/http"
11
 
        "os"
12
 
 
13
 
        "launchpad.net/tomb"
14
 
 
15
 
        "launchpad.net/juju-core/log"
16
 
)
17
 
 
18
 
// Status represents the status of a completed download.
19
 
type Status struct {
20
 
        // File holds the downloaded data on success.
21
 
        File *os.File
22
 
        // Err describes any error encountered while downloading.
23
 
        Err error
24
 
}
25
 
 
26
 
// Download can download a file from the network.
27
 
type Download struct {
28
 
        tomb tomb.Tomb
29
 
        done chan Status
30
 
}
31
 
 
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 {
35
 
        d := &Download{
36
 
                done: make(chan Status),
37
 
        }
38
 
        go d.run(url, dir)
39
 
        return d
40
 
}
41
 
 
42
 
// Stop stops any download that's in progress.
43
 
func (d *Download) Stop() {
44
 
        d.tomb.Kill(nil)
45
 
        d.tomb.Wait()
46
 
}
47
 
 
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
50
 
// the received file.
51
 
func (d *Download) Done() <-chan Status {
52
 
        return d.done
53
 
}
54
 
 
55
 
func (d *Download) run(url, dir string) {
56
 
        defer d.tomb.Done()
57
 
        file, err := download(url, dir)
58
 
        if err != nil {
59
 
                err = fmt.Errorf("cannot download %q: %v", url, err)
60
 
        }
61
 
        status := Status{
62
 
                File: file,
63
 
                Err:  err,
64
 
        }
65
 
        select {
66
 
        case d.done <- status:
67
 
        case <-d.tomb.Dying():
68
 
                cleanTempFile(status.File)
69
 
        }
70
 
}
71
 
 
72
 
func download(url, dir string) (file *os.File, err error) {
73
 
        if dir == "" {
74
 
                dir = os.TempDir()
75
 
        }
76
 
        tempFile, err := ioutil.TempFile(dir, "inprogress-")
77
 
        if err != nil {
78
 
                return nil, err
79
 
        }
80
 
        defer func() {
81
 
                if err != nil {
82
 
                        cleanTempFile(tempFile)
83
 
                }
84
 
        }()
85
 
        // TODO(rog) make the download operation interruptible.
86
 
        resp, err := http.Get(url)
87
 
        if err != nil {
88
 
                return nil, err
89
 
        }
90
 
        defer resp.Body.Close()
91
 
        if resp.StatusCode != http.StatusOK {
92
 
                return nil, fmt.Errorf("bad http response: %v", resp.Status)
93
 
        }
94
 
        _, err = io.Copy(tempFile, resp.Body)
95
 
        if err != nil {
96
 
                return nil, err
97
 
        }
98
 
        if _, err := tempFile.Seek(0, 0); err != nil {
99
 
                return nil, err
100
 
        }
101
 
        return tempFile, nil
102
 
}
103
 
 
104
 
func cleanTempFile(f *os.File) {
105
 
        if f != nil {
106
 
                f.Close()
107
 
                if err := os.Remove(f.Name()); err != nil {
108
 
                        log.Warningf("downloader: cannot remove temp file %q: %v", f.Name(), err)
109
 
                }
110
 
        }
111
 
}