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  }