forked from cloudfoundry/bosh-agent
-
Notifications
You must be signed in to change notification settings - Fork 0
/
http_blob_provider.go
128 lines (103 loc) · 3.35 KB
/
http_blob_provider.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package httpblobprovider
import (
"fmt"
"io"
"net/http"
"os"
"strings"
boshcrypto "github.com/cloudfoundry/bosh-utils/crypto"
bosherr "github.com/cloudfoundry/bosh-utils/errors"
boshsys "github.com/cloudfoundry/bosh-utils/system"
)
var DefaultCryptoAlgorithms = []boshcrypto.Algorithm{boshcrypto.DigestAlgorithmSHA1, boshcrypto.DigestAlgorithmSHA512}
type HTTPBlobImpl struct {
fs boshsys.FileSystem
createAlgorithms []boshcrypto.Algorithm
httpClient *http.Client
}
func NewHTTPBlobImpl(fs boshsys.FileSystem, httpClient *http.Client) *HTTPBlobImpl {
return NewHTTPBlobImplWithDigestAlgorithms(fs, httpClient, DefaultCryptoAlgorithms)
}
func NewHTTPBlobImplWithDigestAlgorithms(fs boshsys.FileSystem, httpClient *http.Client, algorithms []boshcrypto.Algorithm) *HTTPBlobImpl {
return &HTTPBlobImpl{
fs: fs,
createAlgorithms: algorithms,
httpClient: httpClient,
}
}
func (h *HTTPBlobImpl) Upload(signedURL, filepath string, headers map[string]string) (boshcrypto.MultipleDigest, error) {
digest, err := boshcrypto.NewMultipleDigestFromPath(filepath, h.fs, h.createAlgorithms)
if err != nil {
return boshcrypto.MultipleDigest{}, err
}
// Do not close the file in the happy path because the client.Do will handle that.
file, err := h.fs.OpenFile(filepath, os.O_RDONLY, 0)
if err != nil {
return boshcrypto.MultipleDigest{}, err
}
stat, err := h.fs.Stat(filepath)
if err != nil {
defer file.Close()
return boshcrypto.MultipleDigest{}, err
}
req, err := http.NewRequest("PUT", signedURL, file)
if err != nil {
defer file.Close()
return boshcrypto.MultipleDigest{}, err
}
req.Header.Set("Accept", "*/*")
req.Header.Set("Expect", "100-continue")
if headers != nil {
for k, v := range headers {
req.Header.Set(k, v)
}
}
req.ContentLength = stat.Size()
resp, err := h.httpClient.Do(req)
if err != nil {
return boshcrypto.MultipleDigest{}, err
}
if !isSuccess(resp) {
return boshcrypto.MultipleDigest{}, fmt.Errorf("Error executing PUT to %s for %s, response was %#v", signedURL, file.Name(), resp)
}
return digest, nil
}
func (h *HTTPBlobImpl) Get(signedURL string, digest boshcrypto.Digest, headers map[string]string) (string, error) {
file, err := h.fs.TempFile("bosh-http-blob-provider-GET")
if err != nil {
return "", bosherr.WrapError(err, "Creating temporary file")
}
req, err := http.NewRequest("GET", signedURL, strings.NewReader(""))
if err != nil {
defer file.Close()
return "", bosherr.WrapError(err, "Creating Get Request")
}
if headers != nil {
for k, v := range headers {
req.Header.Set(k, v)
}
}
resp, err := h.httpClient.Do(req)
if err != nil {
return file.Name(), bosherr.WrapError(err, "Excuting GET request")
}
if !isSuccess(resp) {
return file.Name(), fmt.Errorf("Error executing GET to %s, response was %#v", signedURL, resp)
}
_, err = io.Copy(file, resp.Body)
if err != nil {
return file.Name(), bosherr.WrapError(err, "Copying response to tempfile")
}
_, err = file.Seek(0, io.SeekStart)
if err != nil {
return file.Name(), bosherr.WrapErrorf(err, "Rewinding file pointer to beginning")
}
err = digest.Verify(file)
if err != nil {
return file.Name(), bosherr.WrapErrorf(err, "Checking downloaded blob digest")
}
return file.Name(), nil
}
func isSuccess(resp *http.Response) bool {
return resp.StatusCode >= 200 && resp.StatusCode < 300
}