--- /dev/null
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !plan9
+
+package main
+
+import (
+ "archive/tar"
+ "archive/zip"
+ "compress/gzip"
+ "crypto/sha256"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+const (
+ currentVersionURL = "https://golang.org/VERSION?m=text"
+ downloadURLPrefix = "https://dl.google.com/go"
+)
+
+// downloadGoVersion downloads and upacks the specific go version to dest/go.
+func downloadGoVersion(version, ops, arch, dest string) error {
+ suffix := "tar.gz"
+ if ops == "windows" {
+ suffix = "zip"
+ }
+ uri := fmt.Sprintf("%s/%s.%s-%s.%s", downloadURLPrefix, version, ops, arch, suffix)
+
+ verbosef("Downloading %s", uri)
+
+ req, err := http.NewRequest("GET", uri, nil)
+ if err != nil {
+ return err
+ }
+ req.Header.Add("User-Agent", fmt.Sprintf("golang.org-getgo/%s", version))
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return fmt.Errorf("Downloading Go from %s failed: %v", uri, err)
+ }
+ if resp.StatusCode > 299 {
+ return fmt.Errorf("Downloading Go from %s failed with HTTP status %s", uri, resp.Status)
+ }
+ defer resp.Body.Close()
+
+ tmpf, err := ioutil.TempFile("", "go")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(tmpf.Name())
+
+ h := sha256.New()
+
+ w := io.MultiWriter(tmpf, h)
+ if _, err := io.Copy(w, resp.Body); err != nil {
+ return err
+ }
+
+ verbosef("Downloading SHA %s.sha256", uri)
+
+ sresp, err := http.Get(uri + ".sha256")
+ if err != nil {
+ return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed: %v", uri, err)
+ }
+ defer sresp.Body.Close()
+ if sresp.StatusCode > 299 {
+ return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed with HTTP status %s", uri, sresp.Status)
+ }
+
+ shasum, err := ioutil.ReadAll(sresp.Body)
+ if err != nil {
+ return err
+ }
+
+ // Check the shasum.
+ sum := fmt.Sprintf("%x", h.Sum(nil))
+ if sum != string(shasum) {
+ return fmt.Errorf("Shasum mismatch %s vs. %s", sum, string(shasum))
+ }
+
+ unpackFunc := unpackTar
+ if ops == "windows" {
+ unpackFunc = unpackZip
+ }
+ if err := unpackFunc(tmpf.Name(), dest); err != nil {
+ return fmt.Errorf("Unpacking Go to %s failed: %v", dest, err)
+ }
+ return nil
+}
+
+func unpack(dest, name string, fi os.FileInfo, r io.Reader) error {
+ if strings.HasPrefix(name, "go/") {
+ name = name[len("go/"):]
+ }
+
+ path := filepath.Join(dest, name)
+ if fi.IsDir() {
+ return os.MkdirAll(path, fi.Mode())
+ }
+
+ f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode())
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ _, err = io.Copy(f, r)
+ return err
+}
+
+func unpackTar(src, dest string) error {
+ r, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer r.Close()
+
+ archive, err := gzip.NewReader(r)
+ if err != nil {
+ return err
+ }
+ defer archive.Close()
+
+ tarReader := tar.NewReader(archive)
+
+ for {
+ header, err := tarReader.Next()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return err
+ }
+
+ if err := unpack(dest, header.Name, header.FileInfo(), tarReader); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func unpackZip(src, dest string) error {
+ zr, err := zip.OpenReader(src)
+ if err != nil {
+ return err
+ }
+
+ for _, f := range zr.File {
+ fr, err := f.Open()
+ if err != nil {
+ return err
+ }
+ if err := unpack(dest, f.Name, f.FileInfo(), fr); err != nil {
+ return err
+ }
+ fr.Close()
+ }
+
+ return nil
+}
+
+func getLatestGoVersion() (string, error) {
+ resp, err := http.Get(currentVersionURL)
+ if err != nil {
+ return "", fmt.Errorf("Getting current Go version failed: %v", err)
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode > 299 {
+ b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 1024))
+ return "", fmt.Errorf("Could not get current Go version: HTTP %d: %q", resp.StatusCode, b)
+ }
+ version, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(string(version)), nil
+}