http.go raw

   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  }
 146