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