cmd/go/internal/modfetch/googlesource: fetch from *.googlesource.com
diff --git a/vendor/cmd/go/internal/modfetch/googlesource/fetch.go b/vendor/cmd/go/internal/modfetch/googlesource/fetch.go
new file mode 100644
index 0000000..a51763b
--- /dev/null
+++ b/vendor/cmd/go/internal/modfetch/googlesource/fetch.go
@@ -0,0 +1,283 @@
+// Copyright 2018 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.
+
+package googlesource
+
+import (
+ "archive/tar"
+ "archive/zip"
+ "bufio"
+ "compress/gzip"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+
+ "cmd/go/internal/modfetch/codehost"
+ web "cmd/go/internal/web2"
+)
+
+func Lookup(path string) (codehost.Repo, error) {
+ i := strings.Index(path, "/")
+ if i+1 == len(path) || !strings.HasSuffix(path[:i+1], ".googlesource.com/") {
+ return nil, fmt.Errorf("not *.googlesource.com/*")
+ }
+ j := strings.Index(path[i+1:], "/")
+ if j >= 0 {
+ path = path[:i+1+j]
+ }
+ r := &repo{
+ root: path,
+ base: "https://" + path,
+ }
+ return r, nil
+}
+
+type repo struct {
+ base string
+ root string
+}
+
+func (r *repo) Root() string {
+ return r.root
+}
+
+func (r *repo) Tags(prefix string) ([]string, error) {
+ var data []byte
+ err := web.Get(r.base+"/+refs/tags/?format=TEXT", web.ReadAllBody(&data))
+ if err != nil {
+ return nil, err
+ }
+ prefix = "refs/tags/" + prefix
+ var tags []string
+ for _, line := range strings.Split(string(data), "\n") {
+ f := strings.Fields(line)
+ if len(f) == 2 && len(f[0]) == 40 && strings.HasPrefix(f[1], prefix) {
+ tags = append(tags, strings.TrimPrefix(f[1], "refs/tags/"))
+ }
+ }
+ return tags, nil
+}
+
+func (r *repo) LatestAt(limit time.Time, branch string) (*codehost.RevInfo, error) {
+ u := r.base + "/+log/" + url.PathEscape(branch) + "?format=JSON&n=2"
+ var n int
+ for u != "" {
+ var body io.ReadCloser
+ err := web.Get(u, web.Body(&body))
+ if err != nil {
+ return nil, err
+ }
+ b := make([]byte, 1)
+ for {
+ _, err := body.Read(b)
+ if err != nil {
+ body.Close()
+ return nil, err
+ }
+ if b[0] == '\n' {
+ break
+ }
+ }
+ var data struct {
+ Log []struct {
+ Commit string
+ Committer struct {
+ Time string
+ }
+ }
+ Next string
+ }
+ err = json.NewDecoder(body).Decode(&data)
+ body.Close()
+ if err != nil {
+ return nil, err
+ }
+ for i := range data.Log {
+ t, err := time.Parse("Mon Jan 02 15:04:05 2006 -0700", data.Log[i].Committer.Time)
+ if err != nil {
+ return nil, err
+ }
+ if !t.After(limit) {
+ info := &codehost.RevInfo{
+ Time: t.UTC(),
+ Name: data.Log[i].Commit,
+ Short: codehost.ShortenSHA1(data.Log[i].Commit),
+ }
+ return info, nil
+ }
+ }
+ u = ""
+ if data.Next != "" {
+ if n == 0 {
+ n = 10
+ } else if n < 1000 {
+ n *= 2
+ }
+ u = r.base + "/+log/" + url.PathEscape(data.Next) + "?format=JSON&n=" + fmt.Sprint(n)
+ }
+ }
+ return nil, fmt.Errorf("no commits")
+}
+
+func (r *repo) Stat(rev string) (*codehost.RevInfo, error) {
+ if !codehost.AllHex(rev) || len(rev) != 40 {
+ return r.LatestAt(time.Date(9999, 1, 1, 0, 0, 0, 0, time.UTC), rev)
+ }
+
+ var body io.ReadCloser
+ u := r.base + "/+show/" + url.PathEscape(rev) + "?format=TEXT"
+ if err := web.Get(u, web.Body(&body)); err != nil {
+ return nil, err
+ }
+ defer body.Close()
+ b := bufio.NewReader(base64.NewDecoder(base64.StdEncoding, body))
+ for {
+ line, err := b.ReadSlice('\n')
+ if err != nil {
+ return nil, err
+ }
+ s := string(line)
+ if s == "\n" {
+ return nil, fmt.Errorf("malformed commit: no committer")
+ }
+ if strings.HasPrefix(s, "committer ") {
+ f := strings.Fields(s)
+ if len(f) >= 3 {
+ v, err := strconv.ParseUint(f[len(f)-2], 10, 64)
+ if err == nil {
+ info := &codehost.RevInfo{
+ Time: time.Unix(int64(v), 0).UTC(),
+ Name: rev,
+ Short: codehost.ShortenSHA1(rev),
+ }
+ return info, nil
+ }
+ }
+ }
+ }
+}
+
+func (r *repo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
+ u := r.base + "/+show/" + url.PathEscape(rev) + "/" + file + "?format=TEXT"
+ var body io.ReadCloser
+ if err := web.Get(u, web.Body(&body)); err != nil {
+ return nil, err
+ }
+ defer body.Close()
+ lr := &io.LimitedReader{R: base64.NewDecoder(base64.StdEncoding, body), N: maxSize + 1}
+ data, err := ioutil.ReadAll(lr)
+ if lr.N <= 0 {
+ return data, fmt.Errorf("too long")
+ }
+ return data, err
+}
+
+type closeRemover struct {
+ *os.File
+}
+
+func (c *closeRemover) Close() error {
+ c.File.Close()
+ os.Remove(c.File.Name())
+ return nil
+}
+
+func (r *repo) ReadZip(rev, subdir string, maxSize int64) (zipstream io.ReadCloser, actualSubdir string, err error) {
+ // Start download of tgz for subdir.
+ if subdir != "" {
+ subdir = "/" + strings.TrimSuffix(subdir, "/")
+ }
+ u := r.base + "/+archive/" + url.PathEscape(rev) + subdir + ".tar.gz"
+ var body io.ReadCloser
+ if err := web.Get(u, web.Body(&body)); err != nil {
+ return nil, "", err
+ }
+ defer body.Close()
+ gz, err := gzip.NewReader(body)
+ if err != nil {
+ return nil, "", err
+ }
+ defer gz.Close()
+ tr := tar.NewReader(gz)
+
+ // Start temporary zip file.
+ f, err := ioutil.TempFile("", "vgo-")
+ if err != nil {
+ return nil, "", err
+ }
+ defer func() {
+ f.Close()
+ if err != nil {
+ os.Remove(f.Name())
+ }
+ }()
+ z := zip.NewWriter(f)
+
+ // Copy files from tgz to zip file.
+ prefix := "googlesource/"
+ haveLICENSE := false
+ for {
+ hdr, err := tr.Next()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, "", fmt.Errorf("reading tgz from gitiles: %v", err)
+ }
+ maxSize -= 512
+ switch hdr.Typeflag {
+ case tar.TypeDir:
+ // ok
+ case tar.TypeReg:
+ if maxSize < hdr.Size {
+ return nil, "", fmt.Errorf("module source tree too big")
+ }
+ maxSize -= hdr.Size
+ if hdr.Name == "LICENSE" {
+ haveLICENSE = true
+ }
+ fw, err := z.Create(prefix + hdr.Name)
+ if err != nil {
+ return nil, "", err
+ }
+ if _, err := io.Copy(fw, tr); err != nil {
+ return nil, "", err
+ }
+ }
+ }
+
+ // Add LICENSE from parent directory if needed.
+ if !haveLICENSE && subdir != "" {
+ if data, err := r.ReadFile(rev, "LICENSE", codehost.MaxLICENSE); err == nil {
+ fw, err := z.Create(prefix + "LICENSE")
+ if err != nil {
+ return nil, "", err
+ }
+ if _, err := fw.Write(data); err != nil {
+ return nil, "", err
+ }
+ }
+ }
+
+ // Finish.
+ if err := z.Close(); err != nil {
+ return nil, "", err
+ }
+ if err := f.Close(); err != nil {
+ return nil, "", err
+ }
+
+ fr, err := os.Open(f.Name())
+ if err != nil {
+ return nil, "", err
+ }
+ return &closeRemover{fr}, subdir, nil
+}