1/* 2Copyright The Helm Authors. 3Licensed under the Apache License, Version 2.0 (the "License"); 4you may not use this file except in compliance with the License. 5You may obtain a copy of the License at 6 7http://www.apache.org/licenses/LICENSE-2.0 8 9Unless required by applicable law or agreed to in writing, software 10distributed under the License is distributed on an "AS IS" BASIS, 11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12See the License for the specific language governing permissions and 13limitations under the License. 14*/ 15 16package getter 17 18import ( 19 "fmt" 20 "io" 21 "net/http" 22 "net/http/httptest" 23 "net/url" 24 "os" 25 "path/filepath" 26 "strconv" 27 "strings" 28 "testing" 29 "time" 30 31 "github.com/pkg/errors" 32 33 "helm.sh/helm/v3/internal/tlsutil" 34 "helm.sh/helm/v3/internal/version" 35 "helm.sh/helm/v3/pkg/cli" 36) 37 38func TestHTTPGetter(t *testing.T) { 39 g, err := NewHTTPGetter(WithURL("http://example.com")) 40 if err != nil { 41 t.Fatal(err) 42 } 43 44 if _, ok := g.(*HTTPGetter); !ok { 45 t.Fatal("Expected NewHTTPGetter to produce an *HTTPGetter") 46 } 47 48 cd := "../../testdata" 49 join := filepath.Join 50 ca, pub, priv := join(cd, "rootca.crt"), join(cd, "crt.pem"), join(cd, "key.pem") 51 insecure := false 52 timeout := time.Second * 5 53 54 // Test with options 55 g, err = NewHTTPGetter( 56 WithBasicAuth("I", "Am"), 57 WithUserAgent("Groot"), 58 WithTLSClientConfig(pub, priv, ca), 59 WithInsecureSkipVerifyTLS(insecure), 60 WithTimeout(timeout), 61 ) 62 if err != nil { 63 t.Fatal(err) 64 } 65 66 hg, ok := g.(*HTTPGetter) 67 if !ok { 68 t.Fatal("expected NewHTTPGetter to produce an *HTTPGetter") 69 } 70 71 if hg.opts.username != "I" { 72 t.Errorf("Expected NewHTTPGetter to contain %q as the username, got %q", "I", hg.opts.username) 73 } 74 75 if hg.opts.password != "Am" { 76 t.Errorf("Expected NewHTTPGetter to contain %q as the password, got %q", "Am", hg.opts.password) 77 } 78 79 if hg.opts.userAgent != "Groot" { 80 t.Errorf("Expected NewHTTPGetter to contain %q as the user agent, got %q", "Groot", hg.opts.userAgent) 81 } 82 83 if hg.opts.certFile != pub { 84 t.Errorf("Expected NewHTTPGetter to contain %q as the public key file, got %q", pub, hg.opts.certFile) 85 } 86 87 if hg.opts.keyFile != priv { 88 t.Errorf("Expected NewHTTPGetter to contain %q as the private key file, got %q", priv, hg.opts.keyFile) 89 } 90 91 if hg.opts.caFile != ca { 92 t.Errorf("Expected NewHTTPGetter to contain %q as the CA file, got %q", ca, hg.opts.caFile) 93 } 94 95 if hg.opts.insecureSkipVerifyTLS != insecure { 96 t.Errorf("Expected NewHTTPGetter to contain %t as InsecureSkipVerifyTLs flag, got %t", false, hg.opts.insecureSkipVerifyTLS) 97 } 98 99 if hg.opts.timeout != timeout { 100 t.Errorf("Expected NewHTTPGetter to contain %s as Timeout flag, got %s", timeout, hg.opts.timeout) 101 } 102 103 // Test if setting insecureSkipVerifyTLS is being passed to the ops 104 insecure = true 105 106 g, err = NewHTTPGetter( 107 WithInsecureSkipVerifyTLS(insecure), 108 ) 109 if err != nil { 110 t.Fatal(err) 111 } 112 113 hg, ok = g.(*HTTPGetter) 114 if !ok { 115 t.Fatal("expected NewHTTPGetter to produce an *HTTPGetter") 116 } 117 118 if hg.opts.insecureSkipVerifyTLS != insecure { 119 t.Errorf("Expected NewHTTPGetter to contain %t as InsecureSkipVerifyTLs flag, got %t", insecure, hg.opts.insecureSkipVerifyTLS) 120 } 121} 122 123func TestDownload(t *testing.T) { 124 expect := "Call me Ishmael" 125 expectedUserAgent := "I am Groot" 126 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 127 defaultUserAgent := "Helm/" + strings.TrimPrefix(version.GetVersion(), "v") 128 if r.UserAgent() != defaultUserAgent { 129 t.Errorf("Expected '%s', got '%s'", defaultUserAgent, r.UserAgent()) 130 } 131 fmt.Fprint(w, expect) 132 })) 133 defer srv.Close() 134 135 g, err := All(cli.New()).ByScheme("http") 136 if err != nil { 137 t.Fatal(err) 138 } 139 got, err := g.Get(srv.URL, WithURL(srv.URL)) 140 if err != nil { 141 t.Fatal(err) 142 } 143 144 if got.String() != expect { 145 t.Errorf("Expected %q, got %q", expect, got.String()) 146 } 147 148 // test with http server 149 basicAuthSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 150 username, password, ok := r.BasicAuth() 151 if !ok || username != "username" || password != "password" { 152 t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) 153 } 154 if r.UserAgent() != expectedUserAgent { 155 t.Errorf("Expected '%s', got '%s'", expectedUserAgent, r.UserAgent()) 156 } 157 fmt.Fprint(w, expect) 158 })) 159 160 defer basicAuthSrv.Close() 161 162 u, _ := url.ParseRequestURI(basicAuthSrv.URL) 163 httpgetter, err := NewHTTPGetter( 164 WithURL(u.String()), 165 WithBasicAuth("username", "password"), 166 WithUserAgent(expectedUserAgent), 167 ) 168 if err != nil { 169 t.Fatal(err) 170 } 171 got, err = httpgetter.Get(u.String()) 172 if err != nil { 173 t.Fatal(err) 174 } 175 176 if got.String() != expect { 177 t.Errorf("Expected %q, got %q", expect, got.String()) 178 } 179} 180 181func TestDownloadTLS(t *testing.T) { 182 cd := "../../testdata" 183 ca, pub, priv := filepath.Join(cd, "rootca.crt"), filepath.Join(cd, "crt.pem"), filepath.Join(cd, "key.pem") 184 185 tlsSrv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 186 tlsConf, err := tlsutil.NewClientTLS(pub, priv, ca) 187 if err != nil { 188 t.Fatal(errors.Wrap(err, "can't create TLS config for client")) 189 } 190 tlsConf.BuildNameToCertificate() 191 tlsConf.ServerName = "helm.sh" 192 tlsSrv.TLS = tlsConf 193 tlsSrv.StartTLS() 194 defer tlsSrv.Close() 195 196 u, _ := url.ParseRequestURI(tlsSrv.URL) 197 g, err := NewHTTPGetter( 198 WithURL(u.String()), 199 WithTLSClientConfig(pub, priv, ca), 200 ) 201 if err != nil { 202 t.Fatal(err) 203 } 204 205 if _, err := g.Get(u.String()); err != nil { 206 t.Error(err) 207 } 208 209 // now test with TLS config being passed along in .Get (see #6635) 210 g, err = NewHTTPGetter() 211 if err != nil { 212 t.Fatal(err) 213 } 214 215 if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig(pub, priv, ca)); err != nil { 216 t.Error(err) 217 } 218 219 // test with only the CA file (see also #6635) 220 g, err = NewHTTPGetter() 221 if err != nil { 222 t.Fatal(err) 223 } 224 225 if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig("", "", ca)); err != nil { 226 t.Error(err) 227 } 228} 229 230func TestDownloadInsecureSkipTLSVerify(t *testing.T) { 231 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 232 defer ts.Close() 233 234 u, _ := url.ParseRequestURI(ts.URL) 235 236 // Ensure the default behaviour did not change 237 g, err := NewHTTPGetter( 238 WithURL(u.String()), 239 ) 240 if err != nil { 241 t.Error(err) 242 } 243 244 if _, err := g.Get(u.String()); err == nil { 245 t.Errorf("Expected Getter to throw an error, got %s", err) 246 } 247 248 // Test certificate check skip 249 g, err = NewHTTPGetter( 250 WithURL(u.String()), 251 WithInsecureSkipVerifyTLS(true), 252 ) 253 if err != nil { 254 t.Error(err) 255 } 256 if _, err = g.Get(u.String()); err != nil { 257 t.Error(err) 258 } 259 260} 261 262func TestHTTPGetterTarDownload(t *testing.T) { 263 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 264 f, _ := os.Open("testdata/empty-0.0.1.tgz") 265 defer f.Close() 266 267 b := make([]byte, 512) 268 f.Read(b) 269 //Get the file size 270 FileStat, _ := f.Stat() 271 FileSize := strconv.FormatInt(FileStat.Size(), 10) 272 273 //Simulating improper header values from bitbucket 274 w.Header().Set("Content-Type", "application/x-tar") 275 w.Header().Set("Content-Encoding", "gzip") 276 w.Header().Set("Content-Length", FileSize) 277 278 f.Seek(0, 0) 279 io.Copy(w, f) 280 })) 281 282 defer srv.Close() 283 284 g, err := NewHTTPGetter(WithURL(srv.URL)) 285 if err != nil { 286 t.Fatal(err) 287 } 288 289 data, _ := g.Get(srv.URL) 290 mimeType := http.DetectContentType(data.Bytes()) 291 292 expectedMimeType := "application/x-gzip" 293 if mimeType != expectedMimeType { 294 t.Fatalf("Expected response with MIME type %s, but got %s", expectedMimeType, mimeType) 295 } 296} 297 298func TestHttpClientInsecureSkipVerify(t *testing.T) { 299 g := HTTPGetter{} 300 g.opts.url = "https://localhost" 301 verifyInsecureSkipVerify(t, g, "Blank HTTPGetter", false) 302 303 g = HTTPGetter{} 304 g.opts.url = "https://localhost" 305 g.opts.caFile = "testdata/ca.crt" 306 verifyInsecureSkipVerify(t, g, "HTTPGetter with ca file", false) 307 308 g = HTTPGetter{} 309 g.opts.url = "https://localhost" 310 g.opts.insecureSkipVerifyTLS = true 311 verifyInsecureSkipVerify(t, g, "HTTPGetter with skip cert verification only", true) 312 313 g = HTTPGetter{} 314 g.opts.url = "https://localhost" 315 g.opts.certFile = "testdata/client.crt" 316 g.opts.keyFile = "testdata/client.key" 317 g.opts.insecureSkipVerifyTLS = true 318 transport := verifyInsecureSkipVerify(t, g, "HTTPGetter with 2 way ssl", true) 319 if len(transport.TLSClientConfig.Certificates) <= 0 { 320 t.Fatal("transport.TLSClientConfig.Certificates is not present") 321 } 322 if transport.TLSClientConfig.ServerName == "" { 323 t.Fatal("TLSClientConfig.ServerName is blank") 324 } 325} 326 327func verifyInsecureSkipVerify(t *testing.T, g HTTPGetter, caseName string, expectedValue bool) *http.Transport { 328 returnVal, err := g.httpClient() 329 330 if err != nil { 331 t.Fatal(err) 332 } 333 334 if returnVal == nil { 335 t.Fatalf("Expected non nil value for http client") 336 } 337 transport := (returnVal.Transport).(*http.Transport) 338 gotValue := false 339 if transport.TLSClientConfig != nil { 340 gotValue = transport.TLSClientConfig.InsecureSkipVerify 341 } 342 if gotValue != expectedValue { 343 t.Fatalf("Case Name = %s\nInsecureSkipVerify did not come as expected. Expected = %t; Got = %v", 344 caseName, expectedValue, gotValue) 345 } 346 return transport 347} 348