github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/web/http.go (about)

     1  // Copyright 2012 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  // +build !cmd_go_bootstrap
     6  
     7  // This code is compiled into the real 'go' binary, but it is not
     8  // compiled into the binary that is built during all.bash, so as
     9  // to avoid needing to build net (and thus use cgo) during the
    10  // bootstrap process.
    11  
    12  package web
    13  
    14  import (
    15  	"crypto/tls"
    16  	"fmt"
    17  	"mime"
    18  	"net/http"
    19  	urlpkg "net/url"
    20  	"os"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/auth"
    25  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg"
    26  	"github.com/gagliardetto/golang-go/cmd/internal/browser"
    27  )
    28  
    29  // impatientInsecureHTTPClient is used in -insecure mode,
    30  // when we're connecting to https servers that might not be there
    31  // or might be using self-signed certificates.
    32  var impatientInsecureHTTPClient = &http.Client{
    33  	Timeout: 5 * time.Second,
    34  	Transport: &http.Transport{
    35  		Proxy: http.ProxyFromEnvironment,
    36  		TLSClientConfig: &tls.Config{
    37  			InsecureSkipVerify: true,
    38  		},
    39  	},
    40  }
    41  
    42  // securityPreservingHTTPClient is like the default HTTP client, but rejects
    43  // redirects to plain-HTTP URLs if the original URL was secure.
    44  var securityPreservingHTTPClient = &http.Client{
    45  	CheckRedirect: func(req *http.Request, via []*http.Request) error {
    46  		if len(via) > 0 && via[0].URL.Scheme == "https" && req.URL.Scheme != "https" {
    47  			lastHop := via[len(via)-1].URL
    48  			return fmt.Errorf("redirected from secure URL %s to insecure URL %s", lastHop, req.URL)
    49  		}
    50  		return nil
    51  	},
    52  }
    53  
    54  func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
    55  	start := time.Now()
    56  
    57  	if url.Scheme == "file" {
    58  		return getFile(url)
    59  	}
    60  
    61  	if os.Getenv("TESTGOPROXY404") == "1" && url.Host == "proxy.golang.org" {
    62  		res := &Response{
    63  			URL:        Redacted(url),
    64  			Status:     "404 testing",
    65  			StatusCode: 404,
    66  			Header:     make(map[string][]string),
    67  			Body:       http.NoBody,
    68  		}
    69  		if cfg.BuildX {
    70  			fmt.Fprintf(os.Stderr, "# get %s: %v (%.3fs)\n", Redacted(url), res.Status, time.Since(start).Seconds())
    71  		}
    72  		return res, nil
    73  	}
    74  
    75  	fetch := func(url *urlpkg.URL) (*urlpkg.URL, *http.Response, error) {
    76  		// Note: The -v build flag does not mean "print logging information",
    77  		// despite its historical misuse for this in GOPATH-based go get.
    78  		// We print extra logging in -x mode instead, which traces what
    79  		// commands are executed.
    80  		if cfg.BuildX {
    81  			fmt.Fprintf(os.Stderr, "# get %s\n", Redacted(url))
    82  		}
    83  
    84  		req, err := http.NewRequest("GET", url.String(), nil)
    85  		if err != nil {
    86  			return nil, nil, err
    87  		}
    88  		if url.Scheme == "https" {
    89  			auth.AddCredentials(req)
    90  		}
    91  
    92  		var res *http.Response
    93  		if security == Insecure && url.Scheme == "https" { // fail earlier
    94  			res, err = impatientInsecureHTTPClient.Do(req)
    95  		} else {
    96  			res, err = securityPreservingHTTPClient.Do(req)
    97  		}
    98  		return url, res, err
    99  	}
   100  
   101  	var (
   102  		fetched *urlpkg.URL
   103  		res     *http.Response
   104  		err     error
   105  	)
   106  	if url.Scheme == "" || url.Scheme == "https" {
   107  		secure := new(urlpkg.URL)
   108  		*secure = *url
   109  		secure.Scheme = "https"
   110  
   111  		fetched, res, err = fetch(secure)
   112  		if err != nil {
   113  			if cfg.BuildX {
   114  				fmt.Fprintf(os.Stderr, "# get %s: %v\n", Redacted(secure), err)
   115  			}
   116  			if security != Insecure || url.Scheme == "https" {
   117  				// HTTPS failed, and we can't fall back to plain HTTP.
   118  				// Report the error from the HTTPS attempt.
   119  				return nil, err
   120  			}
   121  		}
   122  	}
   123  
   124  	if res == nil {
   125  		switch url.Scheme {
   126  		case "http":
   127  			if security == SecureOnly {
   128  				if cfg.BuildX {
   129  					fmt.Fprintf(os.Stderr, "# get %s: insecure\n", Redacted(url))
   130  				}
   131  				return nil, fmt.Errorf("insecure URL: %s", Redacted(url))
   132  			}
   133  		case "":
   134  			if security != Insecure {
   135  				panic("should have returned after HTTPS failure")
   136  			}
   137  		default:
   138  			if cfg.BuildX {
   139  				fmt.Fprintf(os.Stderr, "# get %s: unsupported\n", Redacted(url))
   140  			}
   141  			return nil, fmt.Errorf("unsupported scheme: %s", Redacted(url))
   142  		}
   143  
   144  		insecure := new(urlpkg.URL)
   145  		*insecure = *url
   146  		insecure.Scheme = "http"
   147  		if insecure.User != nil && security != Insecure {
   148  			if cfg.BuildX {
   149  				fmt.Fprintf(os.Stderr, "# get %s: insecure credentials\n", Redacted(insecure))
   150  			}
   151  			return nil, fmt.Errorf("refusing to pass credentials to insecure URL: %s", Redacted(insecure))
   152  		}
   153  
   154  		fetched, res, err = fetch(insecure)
   155  		if err != nil {
   156  			if cfg.BuildX {
   157  				fmt.Fprintf(os.Stderr, "# get %s: %v\n", Redacted(insecure), err)
   158  			}
   159  			// HTTP failed, and we already tried HTTPS if applicable.
   160  			// Report the error from the HTTP attempt.
   161  			return nil, err
   162  		}
   163  	}
   164  
   165  	// Note: accepting a non-200 OK here, so people can serve a
   166  	// meta import in their http 404 page.
   167  	if cfg.BuildX {
   168  		fmt.Fprintf(os.Stderr, "# get %s: %v (%.3fs)\n", Redacted(fetched), res.Status, time.Since(start).Seconds())
   169  	}
   170  
   171  	r := &Response{
   172  		URL:        Redacted(fetched),
   173  		Status:     res.Status,
   174  		StatusCode: res.StatusCode,
   175  		Header:     map[string][]string(res.Header),
   176  		Body:       res.Body,
   177  	}
   178  
   179  	if res.StatusCode != http.StatusOK {
   180  		contentType := res.Header.Get("Content-Type")
   181  		if mediaType, params, _ := mime.ParseMediaType(contentType); mediaType == "text/plain" {
   182  			switch charset := strings.ToLower(params["charset"]); charset {
   183  			case "us-ascii", "utf-8", "":
   184  				// Body claims to be plain text in UTF-8 or a subset thereof.
   185  				// Try to extract a useful error message from it.
   186  				r.errorDetail.r = res.Body
   187  				r.Body = &r.errorDetail
   188  			}
   189  		}
   190  	}
   191  
   192  	return r, nil
   193  }
   194  
   195  func getFile(u *urlpkg.URL) (*Response, error) {
   196  	path, err := urlToFilePath(u)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  	f, err := os.Open(path)
   201  
   202  	if os.IsNotExist(err) {
   203  		return &Response{
   204  			URL:        Redacted(u),
   205  			Status:     http.StatusText(http.StatusNotFound),
   206  			StatusCode: http.StatusNotFound,
   207  			Body:       http.NoBody,
   208  			fileErr:    err,
   209  		}, nil
   210  	}
   211  
   212  	if os.IsPermission(err) {
   213  		return &Response{
   214  			URL:        Redacted(u),
   215  			Status:     http.StatusText(http.StatusForbidden),
   216  			StatusCode: http.StatusForbidden,
   217  			Body:       http.NoBody,
   218  			fileErr:    err,
   219  		}, nil
   220  	}
   221  
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	return &Response{
   227  		URL:        Redacted(u),
   228  		Status:     http.StatusText(http.StatusOK),
   229  		StatusCode: http.StatusOK,
   230  		Body:       f,
   231  	}, nil
   232  }
   233  
   234  func openBrowser(url string) bool { return browser.Open(url) }