cmd/go/internal/modfetch/github: fetch from github
diff --git a/vendor/cmd/go/internal/modfetch/github/fetch.go b/vendor/cmd/go/internal/modfetch/github/fetch.go
new file mode 100644
index 0000000..a88f0e5
--- /dev/null
+++ b/vendor/cmd/go/internal/modfetch/github/fetch.go
@@ -0,0 +1,318 @@
+// 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 github
+
+import (
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+
+	"cmd/go/internal/modfetch/codehost"
+	web "cmd/go/internal/web2"
+)
+
+func Lookup(path string) (codehost.Repo, error) {
+	f := strings.Split(path, "/")
+	if len(f) < 3 || f[0] != "github.com" {
+		return nil, fmt.Errorf("github repo must be github.com/org/project")
+	}
+
+	// Check for moved, renamed, or incorrect case in repository.
+	// Otherwise the API calls all appear to work.
+	// We need to do better here, but it's unclear exactly what.
+	var data struct {
+		FullName string `json:"full_name"`
+	}
+	err := web.Get("https://api.github.com/repos/"+url.PathEscape(f[1])+"/"+url.PathEscape(f[2]), web.DecodeJSON(&data))
+	if err != nil {
+		return nil, err
+	}
+	myFullName := f[1] + "/" + f[2]
+	if myFullName != data.FullName {
+		why := "moved"
+		if strings.EqualFold(myFullName, data.FullName) {
+			why = "wrong case"
+		}
+		return nil, fmt.Errorf("module path of repo is github.com/%s, not %s (%s)", data.FullName, path, why)
+	}
+
+	return newRepo(f[1], f[2]), nil
+}
+
+func newRepo(owner, repository string) codehost.Repo {
+	return &repo{owner, repository}
+}
+
+type repo struct {
+	owner string
+	repo  string
+}
+
+func (r *repo) Root() string {
+	return "github.com/" + r.owner + "/" + r.repo
+}
+
+func (r *repo) Tags(prefix string) ([]string, error) {
+	var tags []string
+	u := "https://api.github.com/repos/" + url.PathEscape(r.owner) + "/" + url.PathEscape(r.repo) + "/tags"
+	for u != "" {
+		var data []struct {
+			Name string
+		}
+		var hdr http.Header
+		err := web.Get(u, web.Header(&hdr), web.DecodeJSON(&data))
+		if err != nil {
+			return nil, err
+		}
+		for _, t := range data {
+			if strings.HasPrefix(t.Name, prefix) {
+				tags = append(tags, t.Name)
+			}
+		}
+		last := u
+		u = ""
+		for _, link := range parseLinks(hdr.Get("Link")) {
+			if link.Rel == "next" && link.URL != last {
+				u = link.URL
+			}
+		}
+	}
+	return tags, nil
+}
+
+func (r *repo) LatestAt(t time.Time, branch string) (*codehost.RevInfo, error) {
+	var commits []struct {
+		SHA    string
+		Commit struct {
+			Committer struct {
+				Date string
+			}
+		}
+	}
+	err := web.Get(
+		"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/commits?sha="+url.QueryEscape(branch)+"&until="+url.QueryEscape(t.UTC().Format("2006-01-02T15:04:05Z"))+"&per_page=2",
+		web.DecodeJSON(&commits),
+	)
+	if err != nil {
+		return nil, err
+	}
+	if len(commits) == 0 {
+		return nil, fmt.Errorf("no commits")
+	}
+	d, err := time.Parse("2006-01-02T15:04:05Z", commits[0].Commit.Committer.Date)
+	if err != nil {
+		return nil, err
+	}
+
+	info := &codehost.RevInfo{
+		Name:  commits[0].SHA,
+		Short: codehost.ShortenSHA1(commits[0].SHA),
+		Time:  d,
+	}
+	return info, nil
+}
+
+func (r *repo) Stat(rev string) (*codehost.RevInfo, error) {
+	var tag string
+	if !codehost.AllHex(rev) {
+		// Resolve tag to rev
+		tag = rev
+		var ref struct {
+			Ref    string
+			Object struct {
+				Type string
+				SHA  string
+				URL  string
+			}
+		}
+		err := web.Get(
+			"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/git/refs/tags/"+tag,
+			web.DecodeJSON(&ref),
+		)
+		if err != nil {
+			return nil, err
+		}
+		switch ref.Object.Type {
+		default:
+			return nil, fmt.Errorf("invalid tag %q: not a commit or tag (%q)", tag, ref.Object.Type)
+
+		case "commit":
+			rev = ref.Object.SHA
+
+		case "tag":
+			var info struct {
+				Object struct {
+					SHA  string
+					Type string
+				}
+			}
+			err = web.Get(
+				ref.Object.URL,
+				web.DecodeJSON(&info),
+			)
+			if err != nil {
+				return nil, err
+			}
+			if info.Object.Type != "commit" {
+				return nil, fmt.Errorf("invalid annotated tag %q: not a commit (%q)", tag, info.Object.Type)
+			}
+			rev = info.Object.SHA
+		}
+		if rev == "" {
+			return nil, fmt.Errorf("invalid tag %q: missing SHA in GitHub response", tag)
+		}
+	}
+
+	var commits []struct {
+		SHA    string
+		Commit struct {
+			Committer struct {
+				Date string
+			}
+		}
+	}
+	err := web.Get(
+		"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/commits?sha="+url.QueryEscape(rev)+"&per_page=2",
+		web.DecodeJSON(&commits),
+	)
+	if err != nil {
+		return nil, err
+	}
+	if len(commits) == 0 {
+		return nil, fmt.Errorf("no commits")
+	}
+	if !strings.HasPrefix(commits[0].SHA, rev) {
+		return nil, fmt.Errorf("wrong rev returned by server")
+	}
+	d, err := time.Parse("2006-01-02T15:04:05Z", commits[0].Commit.Committer.Date)
+	if err != nil {
+		return nil, err
+	}
+	info := &codehost.RevInfo{
+		Name:    commits[0].SHA,
+		Short:   codehost.ShortenSHA1(commits[0].SHA),
+		Version: tag,
+		Time:    d,
+	}
+	return info, nil
+}
+
+func (r *repo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
+	var meta struct {
+		Type        string
+		Size        int64
+		Name        string
+		DownloadURL string `json:"download_url"`
+	}
+	err := web.Get(
+		"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/contents/"+url.PathEscape(file)+"?ref="+url.QueryEscape(rev),
+		web.DecodeJSON(&meta),
+	)
+	if err != nil {
+		return nil, err
+	}
+	if meta.DownloadURL == "" {
+		return nil, fmt.Errorf("no download URL")
+	}
+
+	// TODO: Use maxSize.
+	var body []byte
+	err = web.Get(meta.DownloadURL, web.ReadAllBody(&body))
+	if err != nil {
+		return nil, err
+	}
+	return body, nil
+}
+
+func (r *repo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
+	// TODO: Make web.Get copy to file for us, with limit.
+	var body io.ReadCloser
+	err = web.Get(
+		"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/zipball/"+url.PathEscape(rev),
+		web.Body(&body),
+	)
+	if err != nil {
+		if body != nil {
+			body.Close()
+		}
+		return nil, "", err
+	}
+	return body, "", nil
+}
+
+type link struct {
+	URL string
+	Rel string
+}
+
+func parseLinks(text string) []link {
+	var links []link
+	for text != "" {
+		text = strings.TrimSpace(text)
+		if !strings.HasPrefix(text, "<") {
+			break
+		}
+		i := strings.Index(text, ">")
+		if i < 0 {
+			break
+		}
+		u := text[1:i]
+		text = strings.TrimSpace(text[i+1:])
+	Attrs:
+		for strings.HasPrefix(text, ";") {
+			text = text[1:]
+			j := 0
+			for j < len(text) && text[j] != '=' {
+				if text[j] == ',' || text[j] == ';' || text[j] == '"' {
+					break Attrs
+				}
+				j++
+			}
+			if j >= len(text) {
+				break Attrs
+			}
+			key := strings.TrimSpace(text[:j])
+			text = strings.TrimSpace(text[j+1:])
+			var val string
+			if len(text) > 0 && text[0] == '"' {
+				k := 1
+				for k < len(text) && text[k] != '"' {
+					if text[k] == '\\' && k+1 < len(text) {
+						k++
+					}
+					k++
+				}
+				if k >= len(text) {
+					break Attrs
+				}
+				val, text = text[1:k], text[k+1:]
+			} else {
+				k := 0
+				for k < len(text) && text[k] != ';' && text[k] != ',' {
+					if text[k] == '"' {
+						break Attrs
+					}
+					k++
+				}
+				if k >= len(text) {
+					break Attrs
+				}
+				val, text = text[:k], text[k:]
+			}
+			if key == "rel" {
+				links = append(links, link{URL: u, Rel: strings.TrimSpace(val)})
+			}
+		}
+		text = strings.TrimSpace(text)
+		if !strings.HasPrefix(text, ",") {
+			break
+		}
+		text = text[1:]
+	}
+	return links
+}