Home | History | Annotate | Download | only in web
      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 	"io"
     18 	"io/ioutil"
     19 	"log"
     20 	"net/http"
     21 	"net/url"
     22 	"time"
     23 
     24 	"cmd/go/internal/cfg"
     25 	"cmd/internal/browser"
     26 )
     27 
     28 // httpClient is the default HTTP client, but a variable so it can be
     29 // changed by tests, without modifying http.DefaultClient.
     30 var httpClient = http.DefaultClient
     31 
     32 // impatientInsecureHTTPClient is used in -insecure mode,
     33 // when we're connecting to https servers that might not be there
     34 // or might be using self-signed certificates.
     35 var impatientInsecureHTTPClient = &http.Client{
     36 	Timeout: 5 * time.Second,
     37 	Transport: &http.Transport{
     38 		Proxy: http.ProxyFromEnvironment,
     39 		TLSClientConfig: &tls.Config{
     40 			InsecureSkipVerify: true,
     41 		},
     42 	},
     43 }
     44 
     45 type HTTPError struct {
     46 	status     string
     47 	StatusCode int
     48 	url        string
     49 }
     50 
     51 func (e *HTTPError) Error() string {
     52 	return fmt.Sprintf("%s: %s", e.url, e.status)
     53 }
     54 
     55 // Get returns the data from an HTTP GET request for the given URL.
     56 func Get(url string) ([]byte, error) {
     57 	resp, err := httpClient.Get(url)
     58 	if err != nil {
     59 		return nil, err
     60 	}
     61 	defer resp.Body.Close()
     62 	if resp.StatusCode != 200 {
     63 		err := &HTTPError{status: resp.Status, StatusCode: resp.StatusCode, url: url}
     64 
     65 		return nil, err
     66 	}
     67 	b, err := ioutil.ReadAll(resp.Body)
     68 	if err != nil {
     69 		return nil, fmt.Errorf("%s: %v", url, err)
     70 	}
     71 	return b, nil
     72 }
     73 
     74 // GetMaybeInsecure returns the body of either the importPath's
     75 // https resource or, if unavailable and permitted by the security mode, the http resource.
     76 func GetMaybeInsecure(importPath string, security SecurityMode) (urlStr string, body io.ReadCloser, err error) {
     77 	fetch := func(scheme string) (urlStr string, res *http.Response, err error) {
     78 		u, err := url.Parse(scheme + "://" + importPath)
     79 		if err != nil {
     80 			return "", nil, err
     81 		}
     82 		u.RawQuery = "go-get=1"
     83 		urlStr = u.String()
     84 		if cfg.BuildV {
     85 			log.Printf("Fetching %s", urlStr)
     86 		}
     87 		if security == Insecure && scheme == "https" { // fail earlier
     88 			res, err = impatientInsecureHTTPClient.Get(urlStr)
     89 		} else {
     90 			res, err = httpClient.Get(urlStr)
     91 		}
     92 		return
     93 	}
     94 	closeBody := func(res *http.Response) {
     95 		if res != nil {
     96 			res.Body.Close()
     97 		}
     98 	}
     99 	urlStr, res, err := fetch("https")
    100 	if err != nil {
    101 		if cfg.BuildV {
    102 			log.Printf("https fetch failed: %v", err)
    103 		}
    104 		if security == Insecure {
    105 			closeBody(res)
    106 			urlStr, res, err = fetch("http")
    107 		}
    108 	}
    109 	if err != nil {
    110 		closeBody(res)
    111 		return "", nil, err
    112 	}
    113 	// Note: accepting a non-200 OK here, so people can serve a
    114 	// meta import in their http 404 page.
    115 	if cfg.BuildV {
    116 		log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode)
    117 	}
    118 	return urlStr, res.Body, nil
    119 }
    120 
    121 func QueryEscape(s string) string { return url.QueryEscape(s) }
    122 func OpenBrowser(url string) bool { return browser.Open(url) }
    123