package main import ( "context" "io" "io/ioutil" "net/http" "strings" "time" "github.com/hashicorp/go-retryablehttp" log "github.com/sirupsen/logrus" ) func main() { log.Infof("server is started at") googleRetryableHttpClient := initHTTPClient() resp, body := postFunc(googleRetryableHttpClient, "Hello Body") log.Info("Response: ", resp) log.Info("Body: ", string(body)) } func initHTTPClient() retryablehttp.Client { googleHttpClient := http.Client{ Transport: &http.Transport{ // Proxy: getProxy(configuration.RIDProxy), MaxIdleConnsPerHost: 100, }, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } googleRetryableHttpClient := retryablehttp.Client{ HTTPClient: &googleHttpClient, RetryMax: 3, CheckRetry: CheckRetryPolicy, RetryWaitMin: 0, RetryWaitMax: 0, Backoff: CheckBackoffPolicy, } return googleRetryableHttpClient } func CheckRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) { log.Info("retrying request received: ", err) if ctx.Err() == context.DeadlineExceeded || ctx.Err() == context.Canceled { //return false to retry, as though it exceeded time - still we got response other than 500 //Normally we get 500 in cae of deadline exceeded - i.e. timeout if resp != nil && resp.StatusCode < 500 { log.Info("Not Retrying for an attempt request as got respCode statusCode less than 500: ", resp) return false, err } log.Info("Going for Retrying for an attempt request as either resp is nil or more or equal of 500: ", resp) return true, nil } log.Info("Not Retrying as context deadline did not exceeded: ", resp) return false, err } func CheckBackoffPolicy(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { return time.Second } func postFunc(httpClient retryablehttp.Client, payload string) (*http.Response, []byte) { outBoundCtx, _ := context.WithTimeout(context.Background(), time.Duration(0)*time.Second) res, body := postRequest(httpClient, "http://httpbin.org/post", payload, outBoundCtx) return res, body } func buildRequestHeader() http.Header { toHeader := make(http.Header) toHeader.Set("accept", "application/json") // include trace_id in outgoing request return toHeader } func postRequest(httpClient retryablehttp.Client, urlValue string, payload string, outBoundCtx context.Context) (*http.Response, []byte) { log.Info("sending POST request to %s", urlValue) strpayload := strings.NewReader(payload) outRequest, err := retryablehttp.NewRequest("POST", urlValue, strpayload) if err != nil { log.Error("failed to create request object") return nil, nil } outRequest = outRequest.WithContext(outBoundCtx) outRequest.Header = buildRequestHeader() res, err := httpClient.Do(outRequest) if err != nil { log.Error("failed to send POST request") return nil, nil } defer func() { // Drain the body so that the connection can be reused io.Copy(ioutil.Discard, res.Body) resBodyCloseErr := res.Body.Close() if resBodyCloseErr != nil { log.Error("failed to close the response body") } }() body, err := ioutil.ReadAll(res.Body) if err != nil { log.Error("failed to read body upon POST request") return nil, nil } return res, body }