diff --git a/client/base_client.go b/client/base_client.go index 6e84a19cd..f511985ea 100644 --- a/client/base_client.go +++ b/client/base_client.go @@ -1,6 +1,7 @@ package client import ( + "context" "net/http" "net/url" "time" @@ -13,4 +14,6 @@ type BaseClient interface { headers map[string]interface{}, body ...byte) (*http.Response, error) SetOauth(auth OAuth) OAuth() OAuth + SendRequestWithContext(ctx context.Context, method string, rawURL string, data url.Values, + headers map[string]interface{}, body ...byte) (*http.Response, error) } diff --git a/client/client.go b/client/client.go index d4b58ace9..f4ff8019b 100644 --- a/client/client.go +++ b/client/client.go @@ -21,6 +21,8 @@ import ( var alphanumericRegex *regexp.Regexp var delimitingRegex *regexp.Regexp +var goVersion string +var goVersionOnce sync.Once func init() { alphanumericRegex = regexp.MustCompile(`^[a-zA-Z0-9]*$`) @@ -140,6 +142,12 @@ var userAgentOnce sync.Once func (c *Client) SendRequest(method string, rawURL string, data url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.SendRequestWithContext(context.TODO(), method, rawURL, data, headers, body...) +} + +func (c *Client) SendRequestWithContext(ctx context.Context, method string, rawURL string, data url.Values, + headers map[string]interface{}, body ...byte) (*http.Response, error) { + contentType := extractContentTypeHeader(headers) u, err := url.Parse(rawURL) @@ -167,7 +175,7 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, //data is already processed and information will be added to u(the url) in the //previous step. Now body will solely contain json payload if contentType == jsonContentType { - req, err = http.NewRequest(method, u.String(), bytes.NewBuffer(body)) + req, err = http.NewRequestWithContext(ctx, method, u.String(), bytes.NewBuffer(body)) if err != nil { return nil, err } @@ -203,7 +211,7 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, } if c.OAuth() != nil { oauth := c.OAuth() - token, _ := c.OAuth().GetAccessToken(context.TODO()) + token, _ := c.OAuth().GetAccessToken(ctx) if token != "" { req.Header.Add("Authorization", "Bearer "+token) } @@ -237,3 +245,10 @@ func (c *Client) SetOauth(oauth OAuth) { func (c *Client) OAuth() OAuth { return c.oAuth } + +func getGoVersion() string { + goVersionOnce.Do(func() { + goVersion = runtime.Version() + }) + return goVersion +} diff --git a/client/page_util.go b/client/page_util.go index a9b722f71..0b35e8622 100644 --- a/client/page_util.go +++ b/client/page_util.go @@ -1,6 +1,7 @@ package client import ( + "context" "encoding/json" "fmt" "strings" @@ -25,13 +26,13 @@ func ReadLimits(pageSize *int, limit *int) int { } } -func GetNext(baseUrl string, response interface{}, getNextPage func(nextPageUri string) (interface{}, error)) (interface{}, error) { +func GetNextWithContext(ctx context.Context, baseUrl string, response interface{}, getNextPage func(ctx context.Context, nextPageUri string) (interface{}, error)) (interface{}, error) { nextPageUrl, err := getNextPageUrl(baseUrl, response) if err != nil { return nil, err } - return getNextPage(nextPageUrl) + return getNextPage(ctx, nextPageUrl) } func toMap(s interface{}) (map[string]interface{}, error) { diff --git a/client/page_util_test.go b/client/page_util_test.go index ebfd85509..490b1c66a 100644 --- a/client/page_util_test.go +++ b/client/page_util_test.go @@ -2,6 +2,7 @@ package client import ( "bytes" + "context" "encoding/json" "io" "net/http" @@ -140,7 +141,7 @@ type testMessage struct { To *string `json:"to,omitempty"` } -func getSomething(nextPageUrl string) (interface{}, error) { +func getSomething(ctx context.Context, nextPageUrl string) (interface{}, error) { return nextPageUrl, nil } @@ -151,11 +152,11 @@ func TestPageUtil_GetNext(t *testing.T) { ps := &testResponse{} _ = json.NewDecoder(response.Body).Decode(ps) - nextPageUrl, err := GetNext(baseUrl, ps, getSomething) + nextPageUrl, err := GetNextWithContext(context.TODO(), baseUrl, ps, getSomething) assert.Equal(t, "https://api.twilio.com/2010-04-01/Accounts/ACXX/Messages.json?From=9999999999&PageNumber=&To=4444444444&PageSize=2&Page=1&PageToken=PASMXX", nextPageUrl) assert.Nil(t, err) - nextPageUrl, err = GetNext(baseUrl, nil, getSomething) + nextPageUrl, err = GetNextWithContext(context.TODO(), baseUrl, nil, getSomething) assert.Empty(t, nextPageUrl) assert.Nil(t, err) } diff --git a/client/request_handler.go b/client/request_handler.go index 5c0fdcb8e..71b531c8a 100644 --- a/client/request_handler.go +++ b/client/request_handler.go @@ -2,6 +2,7 @@ package client import ( + "context" "net/http" "net/url" "os" @@ -12,6 +13,7 @@ type RequestHandler struct { Client BaseClient Edge string Region string + ctx context.Context } func NewRequestHandler(client BaseClient) *RequestHandler { @@ -19,6 +21,7 @@ func NewRequestHandler(client BaseClient) *RequestHandler { Client: client, Edge: os.Getenv("TWILIO_EDGE"), Region: os.Getenv("TWILIO_REGION"), + ctx: context.TODO(), } } @@ -82,22 +85,22 @@ func (c *RequestHandler) BuildUrl(rawURL string) (string, error) { return u.String(), nil } -func (c *RequestHandler) Post(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPost, path, bodyData, headers, body...) +func (c *RequestHandler) PostWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodPost, path, bodyData, headers, body...) } -func (c *RequestHandler) Put(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPut, path, bodyData, headers, body...) +func (c *RequestHandler) PutWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodPut, path, bodyData, headers, body...) } -func (c *RequestHandler) Patch(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPatch, path, bodyData, headers, body...) +func (c *RequestHandler) PatchWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodPatch, path, bodyData, headers, body...) } -func (c *RequestHandler) Get(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.sendRequest(http.MethodGet, path, queryData, headers) +func (c *RequestHandler) GetWithContext(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodGet, path, queryData, headers) } -func (c *RequestHandler) Delete(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.sendRequest(http.MethodDelete, path, queryData, headers) +func (c *RequestHandler) DeleteWithContext(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodDelete, path, queryData, headers) } diff --git a/client/request_handler_test.go b/client/request_handler_test.go index 1756ae521..1b1710132 100644 --- a/client/request_handler_test.go +++ b/client/request_handler_test.go @@ -1,6 +1,7 @@ package client_test import ( + "context" "errors" "net/http" "net/http/httptest" @@ -11,9 +12,9 @@ import ( "github.com/twilio/twilio-go/client" ) -func NewRequestHandler(accountSid string, authToken string) *client.RequestHandler { +func NewRequestHandler(accountSid string, authToken string) *client.RequestHandlerWithContext { c := NewClient(accountSid, authToken) - return client.NewRequestHandler(c) + return client.NewRequestHandlerWithContext(c) } func TestRequestHandler_BuildUrlSetRegion(t *testing.T) { @@ -62,7 +63,7 @@ func TestRequestHandler_BuildUrlInvalidCTLCharacter(t *testing.T) { assert.Equal(t, parsedURL, "") } -func assertAndGetURL(t *testing.T, requestHandler *client.RequestHandler, rawURL string) string { +func assertAndGetURL(t *testing.T, requestHandler *client.RequestHandlerWithContext, rawURL string) string { parsedURL, err := requestHandler.BuildUrl(rawURL) assert.Nil(t, err) return parsedURL @@ -83,7 +84,7 @@ func TestRequestHandler_SendGetRequest(t *testing.T) { defer errorServer.Close() requestHandler := NewRequestHandler("user", "pass") - resp, err := requestHandler.Get(errorServer.URL, nil, nil) //nolint:bodyclose + resp, err := requestHandler.GetWithContext(context.TODO(), errorServer.URL, nil, nil) //nolint:bodyclose twilioError := err.(*client.TwilioRestError) assert.Nil(t, resp) assert.Equal(t, 400, twilioError.Status) @@ -108,7 +109,7 @@ func TestRequestHandler_SendPostRequest(t *testing.T) { defer errorServer.Close() requestHandler := NewRequestHandler("user", "pass") - resp, err := requestHandler.Post(errorServer.URL, nil, nil) //nolint:bodyclose + resp, err := requestHandler.PostWithContext(context.TODO(), errorServer.URL, nil, nil) //nolint:bodyclose twilioError := err.(*client.TwilioRestError) assert.Nil(t, resp) assert.Equal(t, 400, twilioError.Status)