github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/go/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 main
    13  
    14  import (
    15  	"fmt"
    16  	"io"
    17  	"io/ioutil"
    18  	"log"
    19  	"net/http"
    20  	"net/url"
    21  )
    22  
    23  // httpClient is the default HTTP client, but a variable so it can be
    24  // changed by tests, without modifying http.DefaultClient.
    25  var httpClient = http.DefaultClient
    26  
    27  type httpError struct {
    28  	status     string
    29  	statusCode int
    30  	url        string
    31  }
    32  
    33  func (e *httpError) Error() string {
    34  	return fmt.Sprintf("%s: %s", e.url, e.status)
    35  }
    36  
    37  // httpGET returns the data from an HTTP GET request for the given URL.
    38  func httpGET(url string) ([]byte, error) {
    39  	resp, err := httpClient.Get(url)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	defer resp.Body.Close()
    44  	if resp.StatusCode != 200 {
    45  		err := &httpError{status: resp.Status, statusCode: resp.StatusCode, url: url}
    46  
    47  		return nil, err
    48  	}
    49  	b, err := ioutil.ReadAll(resp.Body)
    50  	if err != nil {
    51  		return nil, fmt.Errorf("%s: %v", url, err)
    52  	}
    53  	return b, nil
    54  }
    55  
    56  // httpsOrHTTP returns the body of either the importPath's
    57  // https resource or, if unavailable, the http resource.
    58  func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) {
    59  	fetch := func(scheme string) (urlStr string, res *http.Response, err error) {
    60  		u, err := url.Parse(scheme + "://" + importPath)
    61  		if err != nil {
    62  			return "", nil, err
    63  		}
    64  		u.RawQuery = "go-get=1"
    65  		urlStr = u.String()
    66  		if buildV {
    67  			log.Printf("Fetching %s", urlStr)
    68  		}
    69  		res, err = httpClient.Get(urlStr)
    70  		return
    71  	}
    72  	closeBody := func(res *http.Response) {
    73  		if res != nil {
    74  			res.Body.Close()
    75  		}
    76  	}
    77  	urlStr, res, err := fetch("https")
    78  	if err != nil || res.StatusCode != 200 {
    79  		if buildV {
    80  			if err != nil {
    81  				log.Printf("https fetch failed.")
    82  			} else {
    83  				log.Printf("ignoring https fetch with status code %d", res.StatusCode)
    84  			}
    85  		}
    86  		closeBody(res)
    87  		urlStr, res, err = fetch("http")
    88  	}
    89  	if err != nil {
    90  		closeBody(res)
    91  		return "", nil, err
    92  	}
    93  	// Note: accepting a non-200 OK here, so people can serve a
    94  	// meta import in their http 404 page.
    95  	if buildV {
    96  		log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode)
    97  	}
    98  	return urlStr, res.Body, nil
    99  }