-
Notifications
You must be signed in to change notification settings - Fork 260
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e6d6376
commit 7ab686d
Showing
41 changed files
with
3,886 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package client | ||
|
||
import ( | ||
"net/http" | ||
"net/url" | ||
"time" | ||
) | ||
|
||
type BaseClient interface { | ||
SetTimeout(timeout time.Duration) | ||
SendRequest(method string, rawURL string, data url.Values, | ||
headers map[string]interface{}, body ...byte) (*http.Response, error) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// Package client provides internal utilities for the twilio-go client library. | ||
package client | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"regexp" | ||
"runtime" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/sendgrid/sendgrid-go/client/form" | ||
) | ||
|
||
var alphanumericRegex *regexp.Regexp | ||
var delimitingRegex *regexp.Regexp | ||
|
||
func init() { | ||
alphanumericRegex = regexp.MustCompile(`^[a-zA-Z0-9]*$`) | ||
delimitingRegex = regexp.MustCompile(`\.\d+`) | ||
} | ||
|
||
// Credentials store user authentication credentials. | ||
type Credentials struct { | ||
Apikey string | ||
} | ||
|
||
func NewCredentials(apikey string) *Credentials { | ||
return &Credentials{Apikey: apikey} | ||
} | ||
|
||
// Client encapsulates a standard HTTP backend with authorization. | ||
type Client struct { | ||
*Credentials | ||
HTTPClient *http.Client | ||
UserAgentExtensions []string | ||
} | ||
|
||
// default http Client should not follow redirects and return the most recent response. | ||
func defaultHTTPClient() *http.Client { | ||
return &http.Client{ | ||
CheckRedirect: func(req *http.Request, via []*http.Request) error { | ||
return http.ErrUseLastResponse | ||
}, | ||
Timeout: time.Second * 10, | ||
} | ||
} | ||
|
||
func (c *Client) basicAuth() (string, string) { | ||
return c.Credentials.Apikey, "" | ||
} | ||
|
||
func (c *Client) bearerAuth() string { | ||
return c.Credentials.Apikey | ||
} | ||
|
||
// SetTimeout sets the Timeout for HTTP requests. | ||
func (c *Client) SetTimeout(timeout time.Duration) { | ||
if c.HTTPClient == nil { | ||
c.HTTPClient = defaultHTTPClient() | ||
} | ||
c.HTTPClient.Timeout = timeout | ||
} | ||
|
||
func extractContentTypeHeader(headers map[string]interface{}) (cType string) { | ||
headerType, ok := headers["Content-Type"] | ||
if !ok { | ||
return urlEncodedContentType | ||
} | ||
return headerType.(string) | ||
} | ||
|
||
const ( | ||
urlEncodedContentType = "application/x-www-form-urlencoded" | ||
jsonContentType = "application/json" | ||
keepZeros = true | ||
delimiter = '.' | ||
escapee = '\\' | ||
) | ||
|
||
func (c *Client) doWithErr(req *http.Request) (*http.Response, error) { | ||
client := c.HTTPClient | ||
|
||
if client == nil { | ||
client = defaultHTTPClient() | ||
} | ||
|
||
res, err := client.Do(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Note that 3XX response codes are allowed for fetches | ||
if res.StatusCode < 200 || res.StatusCode >= 400 { | ||
err = &SendgridRestError{} | ||
decodeErrs := json.NewDecoder(res.Body) | ||
decodeErr := decodeErrs.Decode(err) | ||
if decodeErr != nil { | ||
err = errors.Wrap(decodeErr, "error decoding the response for an HTTP error code: "+strconv.Itoa(res.StatusCode)) | ||
return nil, err | ||
} | ||
|
||
return nil, err | ||
} | ||
return res, nil | ||
} | ||
|
||
func setBearerToken(req *http.Request, token string) { | ||
req.Header.Set("Authorization", "Bearer "+token) | ||
} | ||
|
||
// SendRequest verifies, constructs, and authorizes an HTTP request. | ||
func (c *Client) SendRequest(method string, rawURL string, data url.Values, | ||
headers map[string]interface{}, body ...byte) (*http.Response, error) { | ||
|
||
contentType := extractContentTypeHeader(headers) | ||
|
||
u, err := url.Parse(rawURL) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
valueReader := &strings.Reader{} | ||
goVersion := runtime.Version() | ||
var req *http.Request | ||
|
||
//For HTTP GET Method there are no body parameters. All other parameters like query, path etc | ||
// are added as information in the url itself. Also while Content-Type is json, we are sending | ||
// json body. In that case, data variable contains all other parameters than body, which is the | ||
//same case as GET method. In that case as well all parameters will be added to url | ||
if method == http.MethodGet || contentType == jsonContentType { | ||
if data != nil { | ||
v, _ := form.EncodeToStringWith(data, delimiter, escapee, keepZeros) | ||
s := delimitingRegex.ReplaceAllString(v, "") | ||
|
||
u.RawQuery = s | ||
} | ||
} | ||
|
||
//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)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} else { | ||
//Here the HTTP POST methods which is not having json content type are processed | ||
//All the values will be added in data and encoded (all body, query, path parameters) | ||
if method == http.MethodPost || method == http.MethodPut { | ||
valueReader = strings.NewReader(data.Encode()) | ||
} | ||
req, err = http.NewRequest(method, u.String(), valueReader) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
} | ||
|
||
setBearerToken(req, c.bearerAuth()) | ||
|
||
// E.g. "User-Agent": "twilio-go/1.0.0 (darwin amd64) go/go1.17.8" | ||
userAgent := fmt.Sprintf("sendgrid-go/%s (%s %s) go/%s", LibraryVersion, runtime.GOOS, runtime.GOARCH, goVersion) | ||
|
||
if len(c.UserAgentExtensions) > 0 { | ||
userAgent += " " + strings.Join(c.UserAgentExtensions, " ") | ||
} | ||
|
||
req.Header.Add("User-Agent", userAgent) | ||
|
||
for k, v := range headers { | ||
req.Header.Add(k, fmt.Sprint(v)) | ||
} | ||
return c.doWithErr(req) | ||
} |
Oops, something went wrong.