github.com/aliyun/credentials-go@v1.4.7/credentials/internal/http/http.go (about)

     1  package http
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net"
    10  	"net/http"
    11  	"net/url"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/alibabacloud-go/debug/debug"
    16  	"github.com/aliyun/credentials-go/credentials/internal/utils"
    17  )
    18  
    19  type Request struct {
    20  	Method         string // http request method
    21  	URL            string // http url
    22  	Protocol       string // http or https
    23  	Host           string // http host
    24  	ReadTimeout    time.Duration
    25  	ConnectTimeout time.Duration
    26  	Proxy          string            // http proxy
    27  	Form           map[string]string // http form
    28  	Body           []byte            // request body for JSON or stream
    29  	Path           string
    30  	Queries        map[string]string
    31  	Headers        map[string]string
    32  }
    33  
    34  func (req *Request) BuildRequestURL() string {
    35  	httpUrl := fmt.Sprintf("%s://%s%s", req.Protocol, req.Host, req.Path)
    36  	if req.URL != "" {
    37  		httpUrl = req.URL
    38  	}
    39  
    40  	querystring := utils.GetURLFormedMap(req.Queries)
    41  	if querystring != "" {
    42  		httpUrl = httpUrl + "?" + querystring
    43  	}
    44  
    45  	return fmt.Sprintf("%s %s", req.Method, httpUrl)
    46  }
    47  
    48  type Response struct {
    49  	StatusCode int
    50  	Headers    map[string]string
    51  	Body       []byte
    52  }
    53  
    54  var newRequest = http.NewRequest
    55  
    56  type do func(req *http.Request) (*http.Response, error)
    57  
    58  var hookDo = func(fn do) do {
    59  	return fn
    60  }
    61  
    62  var debuglog = debug.Init("credential")
    63  
    64  func Do(req *Request) (res *Response, err error) {
    65  	querystring := utils.GetURLFormedMap(req.Queries)
    66  	// do request
    67  	httpUrl := fmt.Sprintf("%s://%s%s?%s", req.Protocol, req.Host, req.Path, querystring)
    68  	if req.URL != "" {
    69  		httpUrl = req.URL
    70  	}
    71  
    72  	var body io.Reader
    73  	if req.Method == "GET" {
    74  		body = strings.NewReader("")
    75  	} else if req.Body != nil {
    76  		body = bytes.NewReader(req.Body)
    77  	} else {
    78  		body = strings.NewReader(utils.GetURLFormedMap(req.Form))
    79  	}
    80  
    81  	httpRequest, err := newRequest(req.Method, httpUrl, body)
    82  	if err != nil {
    83  		return
    84  	}
    85  
    86  	if req.Form != nil {
    87  		httpRequest.Header["Content-Type"] = []string{"application/x-www-form-urlencoded"}
    88  	}
    89  
    90  	for key, value := range req.Headers {
    91  		if value != "" {
    92  			debuglog("> %s: %s", key, value)
    93  			httpRequest.Header.Set(key, value)
    94  		}
    95  	}
    96  
    97  	httpClient := &http.Client{}
    98  
    99  	if req.ReadTimeout != 0 {
   100  		httpClient.Timeout = req.ReadTimeout + req.ConnectTimeout
   101  	}
   102  
   103  	transport := http.DefaultTransport.(*http.Transport).Clone()
   104  	if req.Proxy != "" {
   105  		var proxy *url.URL
   106  		proxy, err = url.Parse(req.Proxy)
   107  		if err != nil {
   108  			return
   109  		}
   110  		transport.Proxy = http.ProxyURL(proxy)
   111  	}
   112  
   113  	if req.ConnectTimeout != 0 {
   114  		transport.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {
   115  			return (&net.Dialer{
   116  				Timeout:   req.ConnectTimeout,
   117  				DualStack: true,
   118  			}).DialContext(ctx, network, address)
   119  		}
   120  	}
   121  
   122  	httpClient.Transport = transport
   123  
   124  	httpResponse, err := hookDo(httpClient.Do)(httpRequest)
   125  	if err != nil {
   126  		return
   127  	}
   128  
   129  	defer httpResponse.Body.Close()
   130  
   131  	responseBody, err := ioutil.ReadAll(httpResponse.Body)
   132  	if err != nil {
   133  		return
   134  	}
   135  	res = &Response{
   136  		StatusCode: httpResponse.StatusCode,
   137  		Headers:    make(map[string]string),
   138  		Body:       responseBody,
   139  	}
   140  	for key, v := range httpResponse.Header {
   141  		res.Headers[key] = v[0]
   142  	}
   143  
   144  	return
   145  }