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 }