Commit cc03ba32 authored by Russ Cox's avatar Russ Cox

cmd/go: split out cmd/go/internal/web

This is one CL in a long sequence of changes to break up the
go command from one package into a plausible group of packages.

This sequence is concerned only with moving code, not changing
or cleaning up code. There will still be more cleanup after this sequence.

The entire sequence will be submitted together: it is not a goal
for the tree to build at every step.

For #18653.

Change-Id: I2f349150659b6ddf6be4c675abba38dfe57ff652
Reviewed-on: https://go-review.googlesource.com/36201Reviewed-by: default avatarDavid Crawshaw <crawshaw@golang.org>
parent 6bc9844b
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
envcmd "cmd/go/internal/env" envcmd "cmd/go/internal/env"
"cmd/go/internal/web"
) )
var cmdBug = &base.Command{ var cmdBug = &base.Command{
...@@ -57,8 +58,8 @@ func runBug(cmd *base.Command, args []string) { ...@@ -57,8 +58,8 @@ func runBug(cmd *base.Command, args []string) {
fmt.Fprintln(&buf, "```") fmt.Fprintln(&buf, "```")
body := buf.String() body := buf.String()
url := "https://github.com/golang/go/issues/new?body=" + queryEscape(body) url := "https://github.com/golang/go/issues/new?body=" + web.QueryEscape(body)
if !openBrowser(url) { if !web.OpenBrowser(url) {
fmt.Print("Please file a new issue at golang.org/issue/new using this template:\n\n") fmt.Print("Please file a new issue at golang.org/issue/new using this template:\n\n")
fmt.Print(body) fmt.Print(body)
} }
...@@ -125,7 +126,7 @@ func printCDetails(w io.Writer) { ...@@ -125,7 +126,7 @@ func printCDetails(w io.Writer) {
} }
func inspectGoVersion(w io.Writer) { func inspectGoVersion(w io.Writer) {
data, err := httpGET("https://golang.org/VERSION?m=text") data, err := web.Get("https://golang.org/VERSION?m=text")
if err != nil { if err != nil {
if cfg.BuildV { if cfg.BuildV {
fmt.Printf("failed to read from golang.org/VERSION: %v\n", err) fmt.Printf("failed to read from golang.org/VERSION: %v\n", err)
......
...@@ -5,11 +5,6 @@ ...@@ -5,11 +5,6 @@
package main package main
import ( import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"cmd/go/internal/work"
"fmt" "fmt"
"go/build" "go/build"
"os" "os"
...@@ -18,6 +13,13 @@ import ( ...@@ -18,6 +13,13 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"cmd/go/internal/web"
"cmd/go/internal/work"
) )
var cmdGet = &base.Command{ var cmdGet = &base.Command{
...@@ -367,9 +369,9 @@ func downloadPackage(p *load.Package) error { ...@@ -367,9 +369,9 @@ func downloadPackage(p *load.Package) error {
err error err error
) )
security := secure security := web.Secure
if *getInsecure { if *getInsecure {
security = insecure security = web.Insecure
} }
if p.Internal.Build.SrcRoot != "" { if p.Internal.Build.SrcRoot != "" {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
// These stubs avoid importing packages with large dependency // These stubs avoid importing packages with large dependency
// trees, like the use of "net/http" in vcs.go. // trees, like the use of "net/http" in vcs.go.
package main package web
import ( import (
"errors" "errors"
...@@ -17,25 +17,21 @@ import ( ...@@ -17,25 +17,21 @@ import (
var errHTTP = errors.New("no http in bootstrap go command") var errHTTP = errors.New("no http in bootstrap go command")
type httpError struct { type HTTPError struct {
statusCode int StatusCode int
} }
func (e *httpError) Error() string { func (e *HTTPError) Error() string {
panic("unreachable") panic("unreachable")
} }
func httpGET(url string) ([]byte, error) { func Get(url string) ([]byte, error) {
return nil, errHTTP return nil, errHTTP
} }
func httpsOrHTTP(importPath string, security securityMode) (string, io.ReadCloser, error) { func GetMaybeInsecure(importPath string, security SecurityMode) (string, io.ReadCloser, error) {
return "", nil, errHTTP return "", nil, errHTTP
} }
func parseMetaGoImports(r io.Reader) ([]metaImport, error) { func QueryEscape(s string) string { panic("unreachable") }
panic("unreachable") func OpenBrowser(url string) bool { panic("unreachable") }
}
func queryEscape(s string) string { panic("unreachable") }
func openBrowser(url string) bool { panic("unreachable") }
...@@ -9,11 +9,9 @@ ...@@ -9,11 +9,9 @@
// to avoid needing to build net (and thus use cgo) during the // to avoid needing to build net (and thus use cgo) during the
// bootstrap process. // bootstrap process.
package main package web
import ( import (
"cmd/go/internal/cfg"
"cmd/internal/browser"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
...@@ -22,6 +20,9 @@ import ( ...@@ -22,6 +20,9 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"time" "time"
"cmd/go/internal/cfg"
"cmd/internal/browser"
) )
// httpClient is the default HTTP client, but a variable so it can be // httpClient is the default HTTP client, but a variable so it can be
...@@ -41,25 +42,25 @@ var impatientInsecureHTTPClient = &http.Client{ ...@@ -41,25 +42,25 @@ var impatientInsecureHTTPClient = &http.Client{
}, },
} }
type httpError struct { type HTTPError struct {
status string status string
statusCode int StatusCode int
url string url string
} }
func (e *httpError) Error() string { func (e *HTTPError) Error() string {
return fmt.Sprintf("%s: %s", e.url, e.status) return fmt.Sprintf("%s: %s", e.url, e.status)
} }
// httpGET returns the data from an HTTP GET request for the given URL. // Get returns the data from an HTTP GET request for the given URL.
func httpGET(url string) ([]byte, error) { func Get(url string) ([]byte, error) {
resp, err := httpClient.Get(url) resp, err := httpClient.Get(url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
err := &httpError{status: resp.Status, statusCode: resp.StatusCode, url: url} err := &HTTPError{status: resp.Status, StatusCode: resp.StatusCode, url: url}
return nil, err return nil, err
} }
...@@ -70,9 +71,9 @@ func httpGET(url string) ([]byte, error) { ...@@ -70,9 +71,9 @@ func httpGET(url string) ([]byte, error) {
return b, nil return b, nil
} }
// httpsOrHTTP returns the body of either the importPath's // GetMaybeInsecure returns the body of either the importPath's
// https resource or, if unavailable, the http resource. // https resource or, if unavailable and permitted by the security mode, the http resource.
func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body io.ReadCloser, err error) { func GetMaybeInsecure(importPath string, security SecurityMode) (urlStr string, body io.ReadCloser, err error) {
fetch := func(scheme string) (urlStr string, res *http.Response, err error) { fetch := func(scheme string) (urlStr string, res *http.Response, err error) {
u, err := url.Parse(scheme + "://" + importPath) u, err := url.Parse(scheme + "://" + importPath)
if err != nil { if err != nil {
...@@ -83,7 +84,7 @@ func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body ...@@ -83,7 +84,7 @@ func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body
if cfg.BuildV { if cfg.BuildV {
log.Printf("Fetching %s", urlStr) log.Printf("Fetching %s", urlStr)
} }
if security == insecure && scheme == "https" { // fail earlier if security == Insecure && scheme == "https" { // fail earlier
res, err = impatientInsecureHTTPClient.Get(urlStr) res, err = impatientInsecureHTTPClient.Get(urlStr)
} else { } else {
res, err = httpClient.Get(urlStr) res, err = httpClient.Get(urlStr)
...@@ -100,7 +101,7 @@ func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body ...@@ -100,7 +101,7 @@ func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body
if cfg.BuildV { if cfg.BuildV {
log.Printf("https fetch failed: %v", err) log.Printf("https fetch failed: %v", err)
} }
if security == insecure { if security == Insecure {
closeBody(res) closeBody(res)
urlStr, res, err = fetch("http") urlStr, res, err = fetch("http")
} }
...@@ -117,5 +118,5 @@ func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body ...@@ -117,5 +118,5 @@ func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body
return urlStr, res.Body, nil return urlStr, res.Body, nil
} }
func queryEscape(s string) string { return url.QueryEscape(s) } func QueryEscape(s string) string { return url.QueryEscape(s) }
func openBrowser(url string) bool { return browser.Open(url) } func OpenBrowser(url string) bool { return browser.Open(url) }
// 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.
// Package web defines helper routines for accessing HTTP/HTTPS resources.
package web
// SecurityMode specifies whether a function should make network
// calls using insecure transports (eg, plain text HTTP).
// The zero value is "secure".
type SecurityMode int
const (
Secure SecurityMode = iota
Insecure
)
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
"cmd/go/internal/web"
) )
// A vcsCmd describes how to use a version control system // A vcsCmd describes how to use a version control system
...@@ -538,19 +539,9 @@ type repoRoot struct { ...@@ -538,19 +539,9 @@ type repoRoot struct {
var httpPrefixRE = regexp.MustCompile(`^https?:`) var httpPrefixRE = regexp.MustCompile(`^https?:`)
// securityMode specifies whether a function should make network
// calls using insecure transports (eg, plain text HTTP).
// The zero value is "secure".
type securityMode int
const (
secure securityMode = iota
insecure
)
// repoRootForImportPath analyzes importPath to determine the // repoRootForImportPath analyzes importPath to determine the
// version control system, and code repository to use. // version control system, and code repository to use.
func repoRootForImportPath(importPath string, security securityMode) (*repoRoot, error) { func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoRoot, error) {
rr, err := repoRootFromVCSPaths(importPath, "", security, vcsPaths) rr, err := repoRootFromVCSPaths(importPath, "", security, vcsPaths)
if err == errUnknownSite { if err == errUnknownSite {
// If there are wildcards, look up the thing before the wildcard, // If there are wildcards, look up the thing before the wildcard,
...@@ -586,7 +577,7 @@ var errUnknownSite = errors.New("dynamic lookup required to find mapping") ...@@ -586,7 +577,7 @@ var errUnknownSite = errors.New("dynamic lookup required to find mapping")
// repoRootFromVCSPaths attempts to map importPath to a repoRoot // repoRootFromVCSPaths attempts to map importPath to a repoRoot
// using the mappings defined in vcsPaths. // using the mappings defined in vcsPaths.
// If scheme is non-empty, that scheme is forced. // If scheme is non-empty, that scheme is forced.
func repoRootFromVCSPaths(importPath, scheme string, security securityMode, vcsPaths []*vcsPath) (*repoRoot, error) { func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, vcsPaths []*vcsPath) (*repoRoot, error) {
// A common error is to use https://packagepath because that's what // A common error is to use https://packagepath because that's what
// hg and git require. Diagnose this helpfully. // hg and git require. Diagnose this helpfully.
if loc := httpPrefixRE.FindStringIndex(importPath); loc != nil { if loc := httpPrefixRE.FindStringIndex(importPath); loc != nil {
...@@ -636,7 +627,7 @@ func repoRootFromVCSPaths(importPath, scheme string, security securityMode, vcsP ...@@ -636,7 +627,7 @@ func repoRootFromVCSPaths(importPath, scheme string, security securityMode, vcsP
match["repo"] = scheme + "://" + match["repo"] match["repo"] = scheme + "://" + match["repo"]
} else { } else {
for _, scheme := range vcs.scheme { for _, scheme := range vcs.scheme {
if security == secure && !vcs.isSecureScheme(scheme) { if security == web.Secure && !vcs.isSecureScheme(scheme) {
continue continue
} }
if vcs.ping(scheme, match["repo"]) == nil { if vcs.ping(scheme, match["repo"]) == nil {
...@@ -660,7 +651,7 @@ func repoRootFromVCSPaths(importPath, scheme string, security securityMode, vcsP ...@@ -660,7 +651,7 @@ func repoRootFromVCSPaths(importPath, scheme string, security securityMode, vcsP
// statically known by repoRootForImportPathStatic. // statically known by repoRootForImportPathStatic.
// //
// This handles custom import paths like "name.tld/pkg/foo" or just "name.tld". // This handles custom import paths like "name.tld/pkg/foo" or just "name.tld".
func repoRootForImportDynamic(importPath string, security securityMode) (*repoRoot, error) { func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*repoRoot, error) {
slash := strings.Index(importPath, "/") slash := strings.Index(importPath, "/")
if slash < 0 { if slash < 0 {
slash = len(importPath) slash = len(importPath)
...@@ -669,10 +660,10 @@ func repoRootForImportDynamic(importPath string, security securityMode) (*repoRo ...@@ -669,10 +660,10 @@ func repoRootForImportDynamic(importPath string, security securityMode) (*repoRo
if !strings.Contains(host, ".") { if !strings.Contains(host, ".") {
return nil, errors.New("import path does not begin with hostname") return nil, errors.New("import path does not begin with hostname")
} }
urlStr, body, err := httpsOrHTTP(importPath, security) urlStr, body, err := web.GetMaybeInsecure(importPath, security)
if err != nil { if err != nil {
msg := "https fetch: %v" msg := "https fetch: %v"
if security == insecure { if security == web.Insecure {
msg = "http/" + msg msg = "http/" + msg
} }
return nil, fmt.Errorf(msg, err) return nil, fmt.Errorf(msg, err)
...@@ -744,7 +735,7 @@ var ( ...@@ -744,7 +735,7 @@ var (
// It is an error if no imports are found. // It is an error if no imports are found.
// urlStr will still be valid if err != nil. // urlStr will still be valid if err != nil.
// The returned urlStr will be of the form "https://golang.org/x/tools?go-get=1" // The returned urlStr will be of the form "https://golang.org/x/tools?go-get=1"
func metaImportsForPrefix(importPrefix string, security securityMode) (urlStr string, imports []metaImport, err error) { func metaImportsForPrefix(importPrefix string, security web.SecurityMode) (urlStr string, imports []metaImport, err error) {
setCache := func(res fetchResult) (fetchResult, error) { setCache := func(res fetchResult) (fetchResult, error) {
fetchCacheMu.Lock() fetchCacheMu.Lock()
defer fetchCacheMu.Unlock() defer fetchCacheMu.Unlock()
...@@ -760,7 +751,7 @@ func metaImportsForPrefix(importPrefix string, security securityMode) (urlStr st ...@@ -760,7 +751,7 @@ func metaImportsForPrefix(importPrefix string, security securityMode) (urlStr st
} }
fetchCacheMu.Unlock() fetchCacheMu.Unlock()
urlStr, body, err := httpsOrHTTP(importPrefix, security) urlStr, body, err := web.GetMaybeInsecure(importPrefix, security)
if err != nil { if err != nil {
return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("fetch %s: %v", urlStr, err)}) return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("fetch %s: %v", urlStr, err)})
} }
...@@ -958,9 +949,9 @@ func bitbucketVCS(match map[string]string) error { ...@@ -958,9 +949,9 @@ func bitbucketVCS(match map[string]string) error {
SCM string `json:"scm"` SCM string `json:"scm"`
} }
url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}") url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}")
data, err := httpGET(url) data, err := web.Get(url)
if err != nil { if err != nil {
if httpErr, ok := err.(*httpError); ok && httpErr.statusCode == 403 { if httpErr, ok := err.(*web.HTTPError); ok && httpErr.StatusCode == 403 {
// this may be a private repository. If so, attempt to determine which // this may be a private repository. If so, attempt to determine which
// VCS it uses. See issue 5375. // VCS it uses. See issue 5375.
root := match["root"] root := match["root"]
...@@ -1000,7 +991,7 @@ func launchpadVCS(match map[string]string) error { ...@@ -1000,7 +991,7 @@ func launchpadVCS(match map[string]string) error {
if match["project"] == "" || match["series"] == "" { if match["project"] == "" || match["series"] == "" {
return nil return nil
} }
_, err := httpGET(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format")) _, err := web.Get(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format"))
if err != nil { if err != nil {
match["root"] = expand(match, "launchpad.net/{project}") match["root"] = expand(match, "launchpad.net/{project}")
match["repo"] = expand(match, "https://{root}") match["repo"] = expand(match, "https://{root}")
......
...@@ -12,6 +12,8 @@ import ( ...@@ -12,6 +12,8 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"testing" "testing"
"cmd/go/internal/web"
) )
// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath. // Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
...@@ -147,7 +149,7 @@ func TestRepoRootForImportPath(t *testing.T) { ...@@ -147,7 +149,7 @@ func TestRepoRootForImportPath(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
got, err := repoRootForImportPath(test.path, secure) got, err := repoRootForImportPath(test.path, web.Secure)
want := test.want want := test.want
if want == nil { if want == nil {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment