github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/thirdparty/http.go (about)

     1  package thirdparty
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"net/http"
     7  	"net/url"
     8  
     9  	"github.com/mongodb/grip"
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  var (
    14  	MaxRedirects = 10
    15  )
    16  
    17  type httpClient interface {
    18  	doGet(string, string, string) (*http.Response, error)
    19  	doPost(string, string, string, interface{}) (*http.Response, error)
    20  	doPut(string, string, string, interface{}) (*http.Response, error)
    21  }
    22  
    23  type liveHttp struct{}
    24  
    25  func shouldRedirectGet(statusCode int) bool {
    26  	switch statusCode {
    27  	case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther, http.StatusTemporaryRedirect:
    28  		return true
    29  	}
    30  	return false
    31  }
    32  
    33  func doFollowingRedirectsWithHeaders(client *http.Client, ireq *http.Request) (resp *http.Response, err error) {
    34  	// Default Go HTTP client silently wipes headers on redirect, so we need to
    35  	// write our own. See http://golang.org/src/pkg/net/http/client.go#L273
    36  	var base *url.URL
    37  	var urlStr string
    38  	req := ireq
    39  	for redirect := 0; ; redirect++ {
    40  		if redirect != 0 {
    41  			req = new(http.Request)
    42  			req.Method = ireq.Method
    43  			// This line is what Go doesn't do. Undocumented but known issue, see
    44  			// https://groups.google.com/forum/#!topic/golang-nuts/OwGvopYXpwE
    45  			req.Header = ireq.Header
    46  
    47  			req.URL, err = base.Parse(urlStr)
    48  			if err != nil {
    49  				break
    50  			}
    51  		}
    52  
    53  		if resp, err = client.Transport.RoundTrip(req); err != nil {
    54  			break
    55  		}
    56  
    57  		if shouldRedirectGet(resp.StatusCode) {
    58  			grip.Warning(resp.Body.Close())
    59  			if urlStr = resp.Header.Get("Location"); urlStr == "" {
    60  				err = errors.Errorf("%d response missing Location header", resp.StatusCode)
    61  				break
    62  			}
    63  
    64  			if redirect+1 >= MaxRedirects {
    65  				return nil, errors.New("Too many redirects")
    66  			}
    67  
    68  			base = req.URL
    69  			continue
    70  		}
    71  		return
    72  	}
    73  
    74  	return
    75  }
    76  
    77  func (self liveHttp) doGet(url string, username string, password string) (*http.Response, error) {
    78  	tr := &http.Transport{
    79  		DisableCompression: true,
    80  		DisableKeepAlives:  false,
    81  	}
    82  
    83  	req, err := http.NewRequest("GET", url, nil)
    84  	if err != nil {
    85  		return nil, errors.Wrap(err, "GET")
    86  	}
    87  
    88  	req.Header.Add("Accept", "*/*")
    89  	req.SetBasicAuth(username, password)
    90  	req.Header.Add("Content-Type", "application/json")
    91  
    92  	client := &http.Client{Transport: tr}
    93  	var resp *http.Response
    94  	resp, err = doFollowingRedirectsWithHeaders(client, req)
    95  	if err != nil {
    96  		return resp, errors.WithStack(err)
    97  	}
    98  	return resp, nil
    99  }
   100  
   101  func (self liveHttp) postOrPut(method string, url string, username string, password string, content interface{}) (*http.Response, error) {
   102  	tr := &http.Transport{
   103  		DisableCompression: true,
   104  		DisableKeepAlives:  false,
   105  	}
   106  
   107  	body := &bytes.Buffer{}
   108  	if err := json.NewEncoder(body).Encode(content); err != nil {
   109  		return nil, errors.Wrap(err, "error encoding request")
   110  	}
   111  
   112  	req, err := http.NewRequest(method, url, body)
   113  	if err != nil {
   114  		return nil, errors.Wrapf(err, "%s", method)
   115  	}
   116  
   117  	req.Header.Add("Accept", "*/*")
   118  	req.SetBasicAuth(username, password)
   119  	req.Header.Add("Content-Type", "application/json")
   120  
   121  	client := &http.Client{Transport: tr}
   122  	var resp *http.Response
   123  	resp, err = doFollowingRedirectsWithHeaders(client, req)
   124  	if err != nil {
   125  		return resp, errors.WithStack(err)
   126  	}
   127  	return resp, nil
   128  }
   129  
   130  func (self liveHttp) doPost(url string, username string, password string, content interface{}) (*http.Response, error) {
   131  	resp, err := self.postOrPut("POST", url, username, password, content)
   132  	return resp, errors.WithStack(err)
   133  }
   134  
   135  func (self liveHttp) doPut(url string, username string, password string, content interface{}) (*http.Response, error) {
   136  	resp, err := self.postOrPut("PUT", url, username, password, content)
   137  	return resp, errors.WithStack(err)
   138  }