cmd/go/internal/web2: second web helper library
diff --git a/vendor/cmd/go/internal/web2/web.go b/vendor/cmd/go/internal/web2/web.go new file mode 100644 index 0000000..0a7b8de --- /dev/null +++ b/vendor/cmd/go/internal/web2/web.go
@@ -0,0 +1,255 @@ +// 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 web2 + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime/debug" + "strings" + "sync" +) + +var TraceGET = false +var webstack = false + +func init() { + flag.BoolVar(&TraceGET, "webtrace", TraceGET, "trace GET requests") + flag.BoolVar(&webstack, "webstack", webstack, "print stack for GET requests") +} + +type netrcLine struct { + machine string + login string + password string +} + +var netrcOnce sync.Once +var netrc []netrcLine + +func parseNetrc(data string) []netrcLine { + var nrc []netrcLine + var l netrcLine + for _, line := range strings.Split(string(data), "\n") { + f := strings.Fields(line) + for i := 0; i < len(f)-1; i += 2 { + switch f[i] { + case "machine": + l.machine = f[i+1] + case "login": + l.login = f[i+1] + case "password": + l.password = f[i+1] + } + } + if l.machine != "" && l.login != "" && l.password != "" { + nrc = append(nrc, l) + l = netrcLine{} + } + } + return nrc +} + +func readNetrc() { + data, err := ioutil.ReadFile(filepath.Join(os.Getenv("HOME"), ".netrc")) + if err != nil { + return + } + netrc = parseNetrc(string(data)) +} + +type getState struct { + req *http.Request + resp *http.Response + body io.ReadCloser + non200ok bool +} + +type Option interface { + option(*getState) error +} + +func Non200OK() Option { + return optionFunc(func(g *getState) error { + g.non200ok = true + return nil + }) +} + +type optionFunc func(*getState) error + +func (f optionFunc) option(g *getState) error { + return f(g) +} + +func DecodeJSON(dst interface{}) Option { + return optionFunc(func(g *getState) error { + if g.resp != nil { + return json.NewDecoder(g.body).Decode(dst) + } + return nil + }) +} + +func ReadAllBody(body *[]byte) Option { + return optionFunc(func(g *getState) error { + if g.resp != nil { + var err error + *body, err = ioutil.ReadAll(g.body) + return err + } + return nil + }) +} + +func Body(body *io.ReadCloser) Option { + return optionFunc(func(g *getState) error { + if g.resp != nil { + *body = g.body + g.body = nil + } + return nil + }) +} + +func Header(hdr *http.Header) Option { + return optionFunc(func(g *getState) error { + if g.resp != nil { + *hdr = CopyHeader(g.resp.Header) + } + return nil + }) +} + +func CopyHeader(hdr http.Header) http.Header { + if hdr == nil { + return nil + } + h2 := make(http.Header) + for k, v := range hdr { + v2 := make([]string, len(v)) + copy(v2, v) + h2[k] = v2 + } + return h2 +} + +var cache struct { + mu sync.Mutex + byURL map[string]*cacheEntry +} + +type cacheEntry struct { + mu sync.Mutex + resp *http.Response + body []byte +} + +var httpDo = http.DefaultClient.Do + +func SetHTTPDoForTesting(do func(*http.Request) (*http.Response, error)) { + if do == nil { + do = http.DefaultClient.Do + } + httpDo = do +} + +func Get(url string, options ...Option) error { + if TraceGET || webstack { + println("GET", url) + if webstack { + println(string(debug.Stack())) + } + } + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + netrcOnce.Do(readNetrc) + for _, l := range netrc { + if l.machine == req.URL.Host { + req.SetBasicAuth(l.login, l.password) + break + } + } + + g := &getState{req: req} + for _, o := range options { + if err := o.option(g); err != nil { + return err + } + } + + cache.mu.Lock() + e := cache.byURL[url] + if e == nil { + e = new(cacheEntry) + if !strings.HasPrefix(url, "file:") { + if cache.byURL == nil { + cache.byURL = make(map[string]*cacheEntry) + } + cache.byURL[url] = e + } + } + cache.mu.Unlock() + + e.mu.Lock() + if strings.HasPrefix(url, "file:") { + body, err := ioutil.ReadFile(req.URL.Path) + if err != nil { + e.mu.Unlock() + return err + } + e.body = body + e.resp = &http.Response{ + StatusCode: 200, + } + } else if e.resp == nil { + resp, err := httpDo(req) + if err != nil { + e.mu.Unlock() + return err + } + e.resp = resp + // TODO: Spool to temp file. + body, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + resp.Body = nil + if err != nil { + e.mu.Unlock() + return err + } + e.body = body + } + g.resp = e.resp + g.body = ioutil.NopCloser(bytes.NewReader(e.body)) + e.mu.Unlock() + + defer func() { + if g.body != nil { + g.body.Close() + } + }() + + if !g.non200ok && g.resp.StatusCode != 200 { + return fmt.Errorf("unexpected status (%s): %v", url, g.resp.Status) + } + + for _, o := range options { + if err := o.option(g); err != nil { + return err + } + } + return err +}
diff --git a/vendor/cmd/go/internal/web2/web_test.go b/vendor/cmd/go/internal/web2/web_test.go new file mode 100644 index 0000000..c6f6b1e --- /dev/null +++ b/vendor/cmd/go/internal/web2/web_test.go
@@ -0,0 +1,35 @@ +// 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 web2 + +import ( + "reflect" + "testing" +) + +var testNetrc = ` +machine api.github.com + login user + password pwd + +machine incomlete.host + login justlogin + +machine test.host +login user2 +password pwd2 +` + +func TestReadNetrc(t *testing.T) { + lines := parseNetrc(testNetrc) + want := []netrcLine{ + {"api.github.com", "user", "pwd"}, + {"test.host", "user2", "pwd2"}, + } + + if !reflect.DeepEqual(lines, want) { + t.Errorf("parseNetrc:\nhave %q\nwant %q", lines, want) + } +}