github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/cmd/getgo/download.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !plan9 6 // +build !plan9 7 8 package main 9 10 import ( 11 "archive/tar" 12 "archive/zip" 13 "compress/gzip" 14 "crypto/sha256" 15 "encoding/json" 16 "fmt" 17 "io" 18 "io/ioutil" 19 "net/http" 20 "os" 21 "path/filepath" 22 "strings" 23 ) 24 25 const ( 26 downloadURLPrefix = "https://dl.google.com/go" 27 ) 28 29 // downloadGoVersion downloads and upacks the specific go version to dest/go. 30 func downloadGoVersion(version, ops, arch, dest string) error { 31 suffix := "tar.gz" 32 if ops == "windows" { 33 suffix = "zip" 34 } 35 uri := fmt.Sprintf("%s/%s.%s-%s.%s", downloadURLPrefix, version, ops, arch, suffix) 36 37 verbosef("Downloading %s", uri) 38 39 req, err := http.NewRequest("GET", uri, nil) 40 if err != nil { 41 return err 42 } 43 req.Header.Add("User-Agent", fmt.Sprintf("golang.org-getgo/%s", version)) 44 45 resp, err := http.DefaultClient.Do(req) 46 if err != nil { 47 return fmt.Errorf("Downloading Go from %s failed: %v", uri, err) 48 } 49 if resp.StatusCode > 299 { 50 return fmt.Errorf("Downloading Go from %s failed with HTTP status %s", uri, resp.Status) 51 } 52 defer resp.Body.Close() 53 54 tmpf, err := ioutil.TempFile("", "go") 55 if err != nil { 56 return err 57 } 58 defer os.Remove(tmpf.Name()) 59 60 h := sha256.New() 61 62 w := io.MultiWriter(tmpf, h) 63 if _, err := io.Copy(w, resp.Body); err != nil { 64 return err 65 } 66 67 verbosef("Downloading SHA %s.sha256", uri) 68 69 sresp, err := http.Get(uri + ".sha256") 70 if err != nil { 71 return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed: %v", uri, err) 72 } 73 defer sresp.Body.Close() 74 if sresp.StatusCode > 299 { 75 return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed with HTTP status %s", uri, sresp.Status) 76 } 77 78 shasum, err := ioutil.ReadAll(sresp.Body) 79 if err != nil { 80 return err 81 } 82 83 // Check the shasum. 84 sum := fmt.Sprintf("%x", h.Sum(nil)) 85 if sum != string(shasum) { 86 return fmt.Errorf("Shasum mismatch %s vs. %s", sum, string(shasum)) 87 } 88 89 unpackFunc := unpackTar 90 if ops == "windows" { 91 unpackFunc = unpackZip 92 } 93 if err := unpackFunc(tmpf.Name(), dest); err != nil { 94 return fmt.Errorf("Unpacking Go to %s failed: %v", dest, err) 95 } 96 return nil 97 } 98 99 func unpack(dest, name string, fi os.FileInfo, r io.Reader) error { 100 if strings.HasPrefix(name, "go/") { 101 name = name[len("go/"):] 102 } 103 104 path := filepath.Join(dest, name) 105 if fi.IsDir() { 106 return os.MkdirAll(path, fi.Mode()) 107 } 108 109 f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode()) 110 if err != nil { 111 return err 112 } 113 defer f.Close() 114 115 _, err = io.Copy(f, r) 116 return err 117 } 118 119 func unpackTar(src, dest string) error { 120 r, err := os.Open(src) 121 if err != nil { 122 return err 123 } 124 defer r.Close() 125 126 archive, err := gzip.NewReader(r) 127 if err != nil { 128 return err 129 } 130 defer archive.Close() 131 132 tarReader := tar.NewReader(archive) 133 134 for { 135 header, err := tarReader.Next() 136 if err == io.EOF { 137 break 138 } else if err != nil { 139 return err 140 } 141 142 if err := unpack(dest, header.Name, header.FileInfo(), tarReader); err != nil { 143 return err 144 } 145 } 146 147 return nil 148 } 149 150 func unpackZip(src, dest string) error { 151 zr, err := zip.OpenReader(src) 152 if err != nil { 153 return err 154 } 155 156 for _, f := range zr.File { 157 fr, err := f.Open() 158 if err != nil { 159 return err 160 } 161 if err := unpack(dest, f.Name, f.FileInfo(), fr); err != nil { 162 return err 163 } 164 fr.Close() 165 } 166 167 return nil 168 } 169 170 func getLatestGoVersion() (string, error) { 171 resp, err := http.Get("https://golang.org/dl/?mode=json") 172 if err != nil { 173 return "", fmt.Errorf("Getting current Go version failed: %v", err) 174 } 175 defer resp.Body.Close() 176 if resp.StatusCode != http.StatusOK { 177 b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 1024)) 178 return "", fmt.Errorf("Could not get current Go release: HTTP %d: %q", resp.StatusCode, b) 179 } 180 var releases []struct { 181 Version string 182 } 183 err = json.NewDecoder(resp.Body).Decode(&releases) 184 if err != nil { 185 return "", err 186 } 187 if len(releases) < 1 { 188 return "", fmt.Errorf("Could not get at least one Go release") 189 } 190 return releases[0].Version, nil 191 }