224 lines
5.2 KiB
Go
224 lines
5.2 KiB
Go
package http
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/ucloud/ucloud-sdk-go/private/utils"
|
|
)
|
|
|
|
var availableHTTPMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTION", "HEAD", "PATCH"}
|
|
|
|
// HttpRequest is the internal http request of sdk, don't use it at your code
|
|
type HttpRequest struct {
|
|
url string
|
|
method string
|
|
queryMap map[string]string
|
|
queryString string
|
|
headers map[string]string
|
|
requestBody []byte
|
|
timeout time.Duration
|
|
}
|
|
|
|
// NewHttpRequest will create a http request
|
|
func NewHttpRequest() *HttpRequest {
|
|
r := &HttpRequest{
|
|
queryMap: make(map[string]string),
|
|
headers: make(map[string]string),
|
|
timeout: DefaultTimeout,
|
|
}
|
|
|
|
for k, v := range DefaultHeaders {
|
|
r.headers[k] = v
|
|
}
|
|
return r
|
|
}
|
|
|
|
// SetURL will set url into request
|
|
func (h *HttpRequest) SetURL(val string) error {
|
|
// check url is valid
|
|
uri, err := url.ParseRequestURI(val)
|
|
if err != nil {
|
|
return errors.Errorf("url is invalid, got %s", val)
|
|
}
|
|
|
|
err = h.SetQueryString(uri.RawQuery)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
h.url = fmt.Sprintf("%s://%s%s", uri.Scheme, uri.Host, uri.Path)
|
|
return nil
|
|
}
|
|
|
|
// GetURL will get request url value
|
|
func (h *HttpRequest) GetURL() string {
|
|
return h.url
|
|
}
|
|
|
|
// SetMethod will set method of current request
|
|
func (h *HttpRequest) SetMethod(val string) error {
|
|
err := utils.CheckStringIn(val, availableHTTPMethods)
|
|
if err != nil {
|
|
return errors.Errorf("method is invalid, %s", err)
|
|
}
|
|
|
|
h.method = strings.ToUpper(val)
|
|
return nil
|
|
}
|
|
|
|
// GetMethod will get request url value
|
|
func (h *HttpRequest) GetMethod() string {
|
|
return h.method
|
|
}
|
|
|
|
// SetQueryString will set query map by query string,
|
|
// it also save as query string attribute to keep query ordered.
|
|
func (h *HttpRequest) SetQueryString(val string) error {
|
|
// check url query is valid
|
|
values, err := url.ParseQuery(val)
|
|
if err != nil {
|
|
return errors.Errorf("url query is invalid, got %s", val)
|
|
}
|
|
|
|
// copy url query into request query map, it will overwrite current query
|
|
for k, v := range values {
|
|
if len(v) > 0 {
|
|
h.SetQuery(k, v[0])
|
|
}
|
|
}
|
|
|
|
h.queryString = val
|
|
return nil
|
|
}
|
|
|
|
// BuildQueryString will return the query string of this request,
|
|
// it will also append key-value of query map after existed query string
|
|
func (h *HttpRequest) BuildQueryString() (string, error) {
|
|
values := url.Values{}
|
|
for k, v := range h.queryMap {
|
|
values.Add(k, v)
|
|
}
|
|
|
|
// if query string is not set by user,
|
|
// otherwise needn't keep them ordered, encode immediately.
|
|
if h.queryString == "" {
|
|
return values.Encode(), nil
|
|
}
|
|
|
|
// exclude query that existed in query string pass by user,
|
|
// to keep ordered from user definition
|
|
existsValues, _ := url.ParseQuery(h.queryString)
|
|
for k := range existsValues {
|
|
values.Del(k)
|
|
}
|
|
|
|
// append query map after existed query string
|
|
qs := h.queryString
|
|
if len(values) > 0 {
|
|
qs += "&" + values.Encode()
|
|
}
|
|
|
|
return qs, nil
|
|
}
|
|
|
|
// SetQuery will store key-value data into query map
|
|
func (h *HttpRequest) SetQuery(k, v string) error {
|
|
if h.queryMap == nil {
|
|
h.queryMap = make(map[string]string)
|
|
}
|
|
h.queryMap[k] = v
|
|
return nil
|
|
}
|
|
|
|
// GetQuery will get value by key from map
|
|
func (h *HttpRequest) GetQuery(k string) string {
|
|
if v, ok := h.queryMap[k]; ok {
|
|
return v
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetQueryMap will get all of query as a map
|
|
func (h *HttpRequest) GetQueryMap() map[string]string {
|
|
return h.queryMap
|
|
}
|
|
|
|
// SetTimeout will set timeout of current request
|
|
func (h *HttpRequest) SetTimeout(val time.Duration) error {
|
|
h.timeout = val
|
|
return nil
|
|
}
|
|
|
|
// GetTimeout will get timeout of current request
|
|
func (h *HttpRequest) GetTimeout() time.Duration {
|
|
return h.timeout
|
|
}
|
|
|
|
// SetHeader will set http header of current request
|
|
func (h *HttpRequest) SetHeader(k, v string) error {
|
|
if h.headers == nil {
|
|
h.headers = make(map[string]string)
|
|
}
|
|
h.headers[k] = v
|
|
return nil
|
|
}
|
|
|
|
// GetHeaderMap wiil get all of header as a map
|
|
func (h *HttpRequest) GetHeaderMap() map[string]string {
|
|
return h.headers
|
|
}
|
|
|
|
// SetRequestBody will set http body of current request
|
|
func (h *HttpRequest) SetRequestBody(val []byte) error {
|
|
h.requestBody = val
|
|
return nil
|
|
}
|
|
|
|
// GetRequestBody will get origin http request ("net/http")
|
|
func (h *HttpRequest) GetRequestBody() []byte {
|
|
return h.requestBody
|
|
}
|
|
|
|
func (h *HttpRequest) String() string {
|
|
if qs, err := h.BuildQueryString(); err == nil {
|
|
return fmt.Sprintf("%s?%s", h.GetURL(), qs)
|
|
}
|
|
return h.GetURL()
|
|
}
|
|
|
|
func (h *HttpRequest) getContentType() string {
|
|
if v, ok := h.headers["Content-Type"]; ok {
|
|
return v
|
|
}
|
|
return string(mimeFormURLEncoded)
|
|
}
|
|
|
|
func (h *HttpRequest) buildHTTPRequest() (*http.Request, error) {
|
|
qs, err := h.BuildQueryString()
|
|
if err != nil {
|
|
return nil, errors.Errorf("cannot build query string, %s", err)
|
|
}
|
|
|
|
var httpReq *http.Request
|
|
if h.getContentType() == string(mimeFormURLEncoded) && len(h.GetRequestBody()) == 0 {
|
|
httpReq, err = http.NewRequest(h.GetMethod(), h.GetURL(), strings.NewReader(qs))
|
|
} else {
|
|
httpReq, err = http.NewRequest(h.GetMethod(), h.String(), bytes.NewReader(h.GetRequestBody()))
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, errors.Errorf("cannot build request, %s", err)
|
|
}
|
|
|
|
for k, v := range utils.MergeMap(DefaultHeaders, h.GetHeaderMap()) {
|
|
httpReq.Header.Set(k, v)
|
|
}
|
|
return httpReq, nil
|
|
}
|