自动更新管控端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

389 lines
8.4 KiB

package httpclient
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/pkg/errors"
"go-mysql-transfer/util/stringutil"
)
const (
_contentTypeForm = 1
_contentTypeJson = 2
)
type FormFile string
// 执行器
type executor struct {
client *HttpClient
criteria *requestCriteria
addr string
method string
expectStatus int
}
type GetOrDeleteExecutor struct {
executor
parameters H
}
type PostOrPutExecutor struct {
executor
body interface{}
contentType int
}
// 全局Criteria覆盖本地Criteria
func (s *executor) overrideCriteria() {
global := s.client.criteria
local := s.criteria
if local.retryCount == 0 {
local.retryCount = global.retryCount
}
if local.retryInterval == 0 {
local.retryInterval = global.retryInterval
}
for _, retryCondition := range global.retryConditions {
local.retryConditions = append(local.retryConditions, retryCondition)
}
for k, v := range global.headers {
if _, exist := local.headers[k]; !exist {
local.headers[k] = v
}
}
}
// 执行Request
func (s *executor) execute(request *http.Request) (*http.Response, error) {
s.overrideCriteria()
for k, v := range s.criteria.headers {
request.Header.Add(k, stringutil.ToString(v))
}
startTime := time.Now().UnixNano()
res, err := s.client.inner.Do(request)
latency := (time.Now().UnixNano() - startTime) / int64(time.Millisecond)
if nil == err {
s.client.logger.Sugar().Infof("请求成功, %s | %s | %d | %d(毫秒)", request.Method, request.URL.String(), res.StatusCode, latency)
}
if s.criteria.retryCount > 0 && s.criteria.needRetry(res) {
for i := 0; i < s.criteria.retryCount; i++ {
s.client.logger.Sugar().Infof("第%d次重试: %s | %s )", i+1, request.Method, request.URL.String())
res, err = s.client.inner.Do(request)
if err != nil {
s.client.logger.Error(err.Error())
}
if !s.criteria.needRetry(res) || (i+1) == s.criteria.retryCount {
return res, err
}
<-time.After(time.Duration(s.criteria.retryInterval) * time.Second)
}
}
if s.expectStatus != 0 && s.expectStatus != res.StatusCode {
defer res.Body.Close()
return nil, errors.Errorf("Response status code : %d (%s)", res.StatusCode, http.StatusText(res.StatusCode))
}
return res, err
}
// 转换Response为string
func (s *executor) responseAsString(response *http.Response) (string, error) {
defer response.Body.Close()
if data, err := ioutil.ReadAll(response.Body); err == nil {
return string(data), nil
}
return "", nil
}
// 转换Response为string
func (s *executor) responseAsEntity(response *http.Response) (*RespondEntity, error) {
defer response.Body.Close()
data, err := ioutil.ReadAll(response.Body)
if nil != err {
return nil, err
}
return &RespondEntity{
statusCode: response.StatusCode,
data: data,
}, nil
}
// 设置请求头
func (r *GetOrDeleteExecutor) AddHeader(name string, value interface{}) *GetOrDeleteExecutor {
r.criteria.AddHeader(name, value)
return r
}
// 设置请求头
func (r *GetOrDeleteExecutor) SetHeaders(values H) *GetOrDeleteExecutor {
r.criteria.AddHeaders(values)
return r
}
// 设置重试次数
func (r *GetOrDeleteExecutor) SetRetryCount(_retryCount int) *GetOrDeleteExecutor {
r.criteria.SetRetryCount(_retryCount)
return r
}
// 设置重试间隔时间,单位为秒
func (r *GetOrDeleteExecutor) SetRetryInterval(_retryInterval int) *GetOrDeleteExecutor {
r.criteria.SetRetryInterval(_retryInterval)
return r
}
// 添加重试条件
func (r *GetOrDeleteExecutor) AddRetryConditionFunc(_retryCondition RetryConditionFunc) *GetOrDeleteExecutor {
r.criteria.AddRetryConditionFunc(_retryCondition)
return r
}
// 设置查询参数
func (r *GetOrDeleteExecutor) AddParameter(name string, value interface{}) *GetOrDeleteExecutor {
r.parameters[name] = value
return r
}
// 设置查询参数
func (r *GetOrDeleteExecutor) AddParameters(values H) *GetOrDeleteExecutor {
for k, v := range values {
r.parameters[k] = v
}
return r
}
// 设置预期请求状态
func (r *GetOrDeleteExecutor) SetExpectStatus(expect int) *GetOrDeleteExecutor {
r.expectStatus = expect
return r
}
// 执行请求
func (r *GetOrDeleteExecutor) Do() (*http.Response, error) {
values := make(url.Values)
for k, v := range r.parameters {
values.Set(k, stringutil.ToString(v))
}
url := stringutil.UrlValuesToQueryString(r.addr, values)
req, err := http.NewRequest(r.method, url, nil)
if nil != err {
return nil, err
}
return r.execute(req)
}
func (r *GetOrDeleteExecutor) DoForString() (string, error) {
res, err := r.Do()
if nil != err {
return "", err
}
return r.responseAsString(res)
}
func (r *GetOrDeleteExecutor) DoForEntity() (*RespondEntity, error) {
res, err := r.Do()
if nil != err {
return nil, err
}
return r.responseAsEntity(res)
}
// 设置请求头
func (r *PostOrPutExecutor) AddHeader(name string, value interface{}) *PostOrPutExecutor {
r.criteria.AddHeader(name, value)
return r
}
// 设置请求头
func (r *PostOrPutExecutor) SetHeaders(values H) *PostOrPutExecutor {
r.criteria.AddHeaders(values)
return r
}
// 设置重试次数
func (r *PostOrPutExecutor) SetRetryCount(_retryCount int) *PostOrPutExecutor {
r.criteria.SetRetryCount(_retryCount)
return r
}
// 设置重试间隔时间,单位为秒
func (r *PostOrPutExecutor) SetRetryInterval(_retryInterval int) *PostOrPutExecutor {
r.criteria.SetRetryInterval(_retryInterval)
return r
}
// 添加重试条件
func (r *PostOrPutExecutor) AddRetryConditionFunc(_retryCondition RetryConditionFunc) *PostOrPutExecutor {
r.criteria.AddRetryConditionFunc(_retryCondition)
return r
}
// 设置预期请求状态
func (r *PostOrPutExecutor) SetExpectStatus(expect int) *PostOrPutExecutor {
r.expectStatus = expect
return r
}
func (r *PostOrPutExecutor) SetBodyAsForm(body H) *PostOrPutExecutor {
r.contentType = _contentTypeForm
r.body = body
return r
}
// 设置请求的contentType 为:
// "application/json"
func (r *PostOrPutExecutor) SetBodyAsJson(body interface{}) *PostOrPutExecutor {
r.contentType = _contentTypeJson
r.body = body
return r
}
// 执行请求
func (r *PostOrPutExecutor) Do() (*http.Response, error) {
if _contentTypeForm == r.contentType {
return r.doFormRequest()
}
var data []byte
switch r.body.(type) {
case string:
ft := r.body.(string)
data = []byte(ft)
case []byte:
data = r.body.([]byte)
default:
temp, err := json.Marshal(r.body)
if err != nil {
return nil, err
}
data = temp
}
req, err := http.NewRequest(r.method, r.addr, bytes.NewReader(data))
if nil != err {
return nil, err
}
req.Header.Add("Content-Type", "application/json")
return r.execute(req)
}
func (r *PostOrPutExecutor) DoForString() (string, error) {
res, err := r.Do()
if nil != err {
return "", err
}
return r.responseAsString(res)
}
func (r *PostOrPutExecutor) DoForEntity() (*RespondEntity, error) {
res, err := r.Do()
if nil != err {
return nil, err
}
return r.responseAsEntity(res)
}
func (r *PostOrPutExecutor) doFormRequest() (*http.Response, error) {
data := r.body.(H)
isMultipart := false
values := make(url.Values)
for k, v := range data {
if _, ok := v.(FormFile); ok {
isMultipart = true
values = nil
break
}
values.Set(k, stringutil.ToString(v))
}
if !isMultipart {
req, err := http.NewRequest(r.method, r.addr, strings.NewReader(values.Encode()))
if nil != err {
return nil, err
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
return r.execute(req)
}
var err error
bodyBuffer := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuffer)
var closings []*os.File
defer func() {
for _, closing := range closings {
closing.Close()
}
}()
for k, v := range data {
switch v.(type) {
case FormFile:
vv := v.(FormFile)
var fw io.Writer
fw, err = bodyWriter.CreateFormFile(k, string(vv))
if err != nil {
break
}
var file *os.File
file, err = os.Open(string(vv))
if err != nil {
break
}
closings = append(closings, file)
_, err = io.Copy(fw, file)
if err != nil {
break
}
default:
if err := bodyWriter.WriteField(k, stringutil.ToString(v)); err != nil {
return nil, err
}
}
}
if err != nil {
return nil, err
}
bodyWriter.Close()
var req *http.Request
req, err = http.NewRequest(r.method, r.addr, bodyBuffer)
if nil != err {
return nil, err
}
req.Header.Add("Content-Type", bodyWriter.FormDataContentType())
return r.execute(req)
}