diff options
author | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
---|---|---|
committer | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
commit | bae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch) | |
tree | ca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/github.com/hashicorp/go-getter/get_http.go | |
parent | 254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff) | |
download | terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.gz terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.zst terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.zip |
Initial transfer of provider code
Diffstat (limited to 'vendor/github.com/hashicorp/go-getter/get_http.go')
-rw-r--r-- | vendor/github.com/hashicorp/go-getter/get_http.go | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/go-getter/get_http.go b/vendor/github.com/hashicorp/go-getter/get_http.go new file mode 100644 index 0000000..3c02034 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_http.go | |||
@@ -0,0 +1,219 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "encoding/xml" | ||
5 | "fmt" | ||
6 | "io" | ||
7 | "io/ioutil" | ||
8 | "net/http" | ||
9 | "net/url" | ||
10 | "os" | ||
11 | "path/filepath" | ||
12 | "strings" | ||
13 | ) | ||
14 | |||
15 | // HttpGetter is a Getter implementation that will download from an HTTP | ||
16 | // endpoint. | ||
17 | // | ||
18 | // For file downloads, HTTP is used directly. | ||
19 | // | ||
20 | // The protocol for downloading a directory from an HTTP endpoing is as follows: | ||
21 | // | ||
22 | // An HTTP GET request is made to the URL with the additional GET parameter | ||
23 | // "terraform-get=1". This lets you handle that scenario specially if you | ||
24 | // wish. The response must be a 2xx. | ||
25 | // | ||
26 | // First, a header is looked for "X-Terraform-Get" which should contain | ||
27 | // a source URL to download. | ||
28 | // | ||
29 | // If the header is not present, then a meta tag is searched for named | ||
30 | // "terraform-get" and the content should be a source URL. | ||
31 | // | ||
32 | // The source URL, whether from the header or meta tag, must be a fully | ||
33 | // formed URL. The shorthand syntax of "github.com/foo/bar" or relative | ||
34 | // paths are not allowed. | ||
35 | type HttpGetter struct { | ||
36 | // Netrc, if true, will lookup and use auth information found | ||
37 | // in the user's netrc file if available. | ||
38 | Netrc bool | ||
39 | } | ||
40 | |||
41 | func (g *HttpGetter) ClientMode(u *url.URL) (ClientMode, error) { | ||
42 | if strings.HasSuffix(u.Path, "/") { | ||
43 | return ClientModeDir, nil | ||
44 | } | ||
45 | return ClientModeFile, nil | ||
46 | } | ||
47 | |||
48 | func (g *HttpGetter) Get(dst string, u *url.URL) error { | ||
49 | // Copy the URL so we can modify it | ||
50 | var newU url.URL = *u | ||
51 | u = &newU | ||
52 | |||
53 | if g.Netrc { | ||
54 | // Add auth from netrc if we can | ||
55 | if err := addAuthFromNetrc(u); err != nil { | ||
56 | return err | ||
57 | } | ||
58 | } | ||
59 | |||
60 | // Add terraform-get to the parameter. | ||
61 | q := u.Query() | ||
62 | q.Add("terraform-get", "1") | ||
63 | u.RawQuery = q.Encode() | ||
64 | |||
65 | // Get the URL | ||
66 | resp, err := http.Get(u.String()) | ||
67 | if err != nil { | ||
68 | return err | ||
69 | } | ||
70 | defer resp.Body.Close() | ||
71 | if resp.StatusCode < 200 || resp.StatusCode >= 300 { | ||
72 | return fmt.Errorf("bad response code: %d", resp.StatusCode) | ||
73 | } | ||
74 | |||
75 | // Extract the source URL | ||
76 | var source string | ||
77 | if v := resp.Header.Get("X-Terraform-Get"); v != "" { | ||
78 | source = v | ||
79 | } else { | ||
80 | source, err = g.parseMeta(resp.Body) | ||
81 | if err != nil { | ||
82 | return err | ||
83 | } | ||
84 | } | ||
85 | if source == "" { | ||
86 | return fmt.Errorf("no source URL was returned") | ||
87 | } | ||
88 | |||
89 | // If there is a subdir component, then we download the root separately | ||
90 | // into a temporary directory, then copy over the proper subdir. | ||
91 | source, subDir := SourceDirSubdir(source) | ||
92 | if subDir == "" { | ||
93 | return Get(dst, source) | ||
94 | } | ||
95 | |||
96 | // We have a subdir, time to jump some hoops | ||
97 | return g.getSubdir(dst, source, subDir) | ||
98 | } | ||
99 | |||
100 | func (g *HttpGetter) GetFile(dst string, u *url.URL) error { | ||
101 | resp, err := http.Get(u.String()) | ||
102 | if err != nil { | ||
103 | return err | ||
104 | } | ||
105 | defer resp.Body.Close() | ||
106 | if resp.StatusCode != 200 { | ||
107 | return fmt.Errorf("bad response code: %d", resp.StatusCode) | ||
108 | } | ||
109 | |||
110 | // Create all the parent directories | ||
111 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
112 | return err | ||
113 | } | ||
114 | |||
115 | f, err := os.Create(dst) | ||
116 | if err != nil { | ||
117 | return err | ||
118 | } | ||
119 | defer f.Close() | ||
120 | |||
121 | _, err = io.Copy(f, resp.Body) | ||
122 | return err | ||
123 | } | ||
124 | |||
125 | // getSubdir downloads the source into the destination, but with | ||
126 | // the proper subdir. | ||
127 | func (g *HttpGetter) getSubdir(dst, source, subDir string) error { | ||
128 | // Create a temporary directory to store the full source | ||
129 | td, err := ioutil.TempDir("", "tf") | ||
130 | if err != nil { | ||
131 | return err | ||
132 | } | ||
133 | defer os.RemoveAll(td) | ||
134 | |||
135 | // Download that into the given directory | ||
136 | if err := Get(td, source); err != nil { | ||
137 | return err | ||
138 | } | ||
139 | |||
140 | // Make sure the subdir path actually exists | ||
141 | sourcePath := filepath.Join(td, subDir) | ||
142 | if _, err := os.Stat(sourcePath); err != nil { | ||
143 | return fmt.Errorf( | ||
144 | "Error downloading %s: %s", source, err) | ||
145 | } | ||
146 | |||
147 | // Copy the subdirectory into our actual destination. | ||
148 | if err := os.RemoveAll(dst); err != nil { | ||
149 | return err | ||
150 | } | ||
151 | |||
152 | // Make the final destination | ||
153 | if err := os.MkdirAll(dst, 0755); err != nil { | ||
154 | return err | ||
155 | } | ||
156 | |||
157 | return copyDir(dst, sourcePath, false) | ||
158 | } | ||
159 | |||
160 | // parseMeta looks for the first meta tag in the given reader that | ||
161 | // will give us the source URL. | ||
162 | func (g *HttpGetter) parseMeta(r io.Reader) (string, error) { | ||
163 | d := xml.NewDecoder(r) | ||
164 | d.CharsetReader = charsetReader | ||
165 | d.Strict = false | ||
166 | var err error | ||
167 | var t xml.Token | ||
168 | for { | ||
169 | t, err = d.Token() | ||
170 | if err != nil { | ||
171 | if err == io.EOF { | ||
172 | err = nil | ||
173 | } | ||
174 | return "", err | ||
175 | } | ||
176 | if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { | ||
177 | return "", nil | ||
178 | } | ||
179 | if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { | ||
180 | return "", nil | ||
181 | } | ||
182 | e, ok := t.(xml.StartElement) | ||
183 | if !ok || !strings.EqualFold(e.Name.Local, "meta") { | ||
184 | continue | ||
185 | } | ||
186 | if attrValue(e.Attr, "name") != "terraform-get" { | ||
187 | continue | ||
188 | } | ||
189 | if f := attrValue(e.Attr, "content"); f != "" { | ||
190 | return f, nil | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | |||
195 | // attrValue returns the attribute value for the case-insensitive key | ||
196 | // `name', or the empty string if nothing is found. | ||
197 | func attrValue(attrs []xml.Attr, name string) string { | ||
198 | for _, a := range attrs { | ||
199 | if strings.EqualFold(a.Name.Local, name) { | ||
200 | return a.Value | ||
201 | } | ||
202 | } | ||
203 | return "" | ||
204 | } | ||
205 | |||
206 | // charsetReader returns a reader for the given charset. Currently | ||
207 | // it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful | ||
208 | // error which is printed by go get, so the user can find why the package | ||
209 | // wasn't downloaded if the encoding is not supported. Note that, in | ||
210 | // order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters | ||
211 | // greater than 0x7f are not rejected). | ||
212 | func charsetReader(charset string, input io.Reader) (io.Reader, error) { | ||
213 | switch strings.ToLower(charset) { | ||
214 | case "ascii": | ||
215 | return input, nil | ||
216 | default: | ||
217 | return nil, fmt.Errorf("can't decode XML document using charset %q", charset) | ||
218 | } | ||
219 | } | ||