github.com/XiaoMi/Gaea@v1.2.5/util/requests/api.go (about) 1 // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package requests 16 17 import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "net" 24 "net/http" 25 "net/url" 26 "sync/atomic" 27 "time" 28 29 "github.com/XiaoMi/Gaea/log" 30 ) 31 32 // Supported http methods 33 const ( 34 Get string = "GET" 35 Post string = "POST" 36 Put string = "PUT" 37 Patch string = "PATCH" 38 Delete string = "DELETE" 39 ) 40 41 // default global client,safe for concurrent use by multiple goroutines 42 var defaultClient *http.Client 43 44 func init() { 45 var dials uint64 46 tr := &http.Transport{} 47 tr.Dial = func(network, addr string) (net.Conn, error) { 48 c, err := net.DialTimeout(network, addr, time.Second*10) 49 if err == nil { 50 log.Debug("rpc: dial new connection to %s, dials: %d", addr, atomic.AddUint64(&dials, 1)-1) 51 } 52 return c, err 53 } 54 defaultClient = &http.Client{ 55 Transport: tr, 56 Timeout: time.Second * 30, 57 } 58 go func() { 59 for { 60 time.Sleep(time.Minute) 61 tr.CloseIdleConnections() 62 } 63 }() 64 } 65 66 // Request request info 67 type Request struct { 68 User string 69 Password string 70 Method string 71 URL string 72 Header map[string]string 73 Params map[string]string 74 Body []byte 75 } 76 77 // NewRequest return Request 78 func NewRequest(url, method string, header map[string]string, params map[string]string, data []byte) *Request { 79 return &Request{Method: method, URL: url, Header: header, Params: params, Body: data} 80 } 81 82 // SetBasicAuth set basic auth of request 83 func (req *Request) SetBasicAuth(user, password string) { 84 req.User = user 85 req.Password = password 86 } 87 88 // Response response info 89 type Response struct { 90 StatusCode int 91 Header map[string][]string 92 Body []byte 93 } 94 95 // AddParameters adds query parameters to the URL. 96 func AddParameters(baseURL string, queryParams map[string]string) string { 97 baseURL += "?" 98 params := url.Values{} 99 for key, value := range queryParams { 100 params.Add(key, value) 101 } 102 return baseURL + params.Encode() 103 } 104 105 // BuildHTTPRequest build a http request object 106 func BuildHTTPRequest(request *Request) (*http.Request, error) { 107 // handle parameters 108 if len(request.Params) > 0 { 109 request.URL = AddParameters(request.URL, request.Params) 110 } 111 // build http request 112 httpReq, err := http.NewRequest(request.Method, request.URL, bytes.NewReader(request.Body)) 113 if err != nil { 114 return nil, err 115 } 116 // set basic auth 117 if request.User != "" && request.Password != "" { 118 httpReq.SetBasicAuth(request.User, request.Password) 119 } 120 121 // build http header 122 for k, v := range request.Header { 123 httpReq.Header.Set(k, v) 124 } 125 126 // default json 127 _, ok := request.Header["Content-Type"] 128 if len(request.Body) > 0 && !ok { 129 httpReq.Header.Set("Content-Type", "application/json") 130 } 131 return httpReq, nil 132 } 133 134 func buildResponse(res *http.Response) (*Response, error) { 135 body, err := ioutil.ReadAll(res.Body) 136 if err != nil { 137 return nil, err 138 } 139 r := &Response{ 140 StatusCode: res.StatusCode, 141 Body: body, 142 Header: res.Header, 143 } 144 return r, nil 145 } 146 147 // Send send http request 148 func Send(request *Request) (*Response, error) { 149 var start = time.Now() 150 151 // build http request 152 httpReq, err := BuildHTTPRequest(request) 153 if err != nil { 154 return nil, err 155 } 156 157 // send http request 158 rsp, err := defaultClient.Do(httpReq) 159 if err != nil { 160 return nil, err 161 } 162 defer func() { 163 io.Copy(ioutil.Discard, rsp.Body) 164 // close http response 165 rsp.Body.Close() 166 log.Debug("call rpc [%s] %s in %v", httpReq.Method, httpReq.URL, time.Since(start)) 167 }() 168 169 // build response 170 r, err := buildResponse(rsp) 171 return r, err 172 } 173 174 // SendPut send put http request 175 func SendPut(url, user, password string) error { 176 req := NewRequest(url, Put, nil, nil, nil) 177 req.SetBasicAuth(user, password) 178 179 resp, err := Send(req) 180 if err != nil { 181 return err 182 } 183 if resp.StatusCode != http.StatusOK { 184 return errors.New(string(resp.Body)) 185 } 186 return nil 187 } 188 189 // SendGet send get http request 190 func SendGet(url, user, password string) (*Response, error) { 191 req := NewRequest(url, Get, nil, nil, nil) 192 req.SetBasicAuth(user, password) 193 194 resp, err := Send(req) 195 if err != nil || resp.StatusCode != http.StatusOK { 196 return nil, err 197 } 198 return resp, nil 199 } 200 201 // EncodeURL encode url 202 func EncodeURL(host string, format string, args ...interface{}) string { 203 var u url.URL 204 u.Scheme = "http" 205 u.Host = host 206 u.Path = fmt.Sprintf(format, args...) 207 return u.String() 208 }