diff --git a/lib/client.go b/lib/client.go index 61df58e..0f805a1 100644 --- a/lib/client.go +++ b/lib/client.go @@ -1,6 +1,7 @@ package lib import ( + "bytes" "crypto/tls" "encoding/json" "errors" @@ -168,8 +169,25 @@ func (c *Client) do(req *http.Request, data interface{}) error { // Throttle http requests to avoid hitting Vultr's API rate-limit c.bucket.Wait(1) + // Request body gets drained on each read so we + // need to save it's content for retrying requests + var err error + var requestBody []byte + if req.Body != nil { + requestBody, err = ioutil.ReadAll(req.Body) + if err != nil { + return fmt.Errorf("Error reading request body: %v", err) + } + req.Body.Close() + } + var apiError error for tryCount := 1; tryCount <= c.MaxAttempts; tryCount++ { + // Restore request body to the original state + if requestBody != nil { + req.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody)) + } + resp, err := c.client.Do(req) if err != nil { return err diff --git a/lib/client_test.go b/lib/client_test.go index 6af852a..ba5cfd2 100644 --- a/lib/client_test.go +++ b/lib/client_test.go @@ -132,3 +132,27 @@ func Test_Client_Retry(t *testing.T) { wg.Wait() } + +// Test that body state is preserved when retrying POST requests +func Test_Client_Retry_Post(t *testing.T) { + server := getTestServerThrottled(`{"SUBID":"5671234"}`) + defer server.Close() + + options := Options{ + Endpoint: server.URL, + MaxRetries: 5, + } + client := NewClient("test-key", &options) + + var wg sync.WaitGroup + wg.Add(10) + for i := 0; i < 10; i++ { + go func() { + defer wg.Done() + _, err := client.CreateBlockStorage("delta", 33, 150) + assert.NoError(t, err) + }() + } + + wg.Wait() +}