123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- package httpclient
- import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- httpURL "net/url"
- "time"
- "git.bvbej.com/bvbej/base-golang/pkg/errors"
- "git.bvbej.com/bvbej/base-golang/pkg/trace"
- )
- const (
- // DefaultTTL 一次http请求最长执行1分钟
- DefaultTTL = time.Minute
- )
- // Get get 请求
- func Get(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
- return withoutBody(http.MethodGet, url, form, options...)
- }
- // Delete delete 请求
- func Delete(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
- return withoutBody(http.MethodDelete, url, form, options...)
- }
- func withoutBody(method, url string, form httpURL.Values, options ...Option) (body []byte, err error) {
- if url == "" {
- return nil, errors.New("url required")
- }
- if len(form) > 0 {
- if url, err = addFormValuesIntoURL(url, form); err != nil {
- return
- }
- }
- ts := time.Now()
- opt := getOption()
- defer func() {
- if opt.trace != nil {
- opt.dialog.Success = err == nil
- opt.dialog.CostSeconds = time.Since(ts).Seconds()
- opt.trace.AppendDialog(opt.dialog)
- }
- releaseOption(opt)
- }()
- for _, f := range options {
- f(opt)
- }
- opt.header["Content-Type"] = []string{"application/x-www-form-urlencoded; charset=utf-8"}
- if opt.trace != nil {
- opt.header[trace.Header] = []string{opt.trace.ID()}
- }
- ttl := opt.ttl
- if ttl <= 0 {
- ttl = DefaultTTL
- }
- ctx, cancel := context.WithTimeout(context.Background(), ttl)
- defer cancel()
- if opt.dialog != nil {
- decodedURL, _ := httpURL.QueryUnescape(url)
- opt.dialog.Request = &trace.Request{
- TTL: ttl.String(),
- Method: method,
- DecodedURL: decodedURL,
- Header: opt.header,
- }
- }
- retryTimes := opt.retryTimes
- if retryTimes <= 0 {
- retryTimes = DefaultRetryTimes
- }
- retryDelay := opt.retryDelay
- if retryDelay <= 0 {
- retryDelay = DefaultRetryDelay
- }
- var httpCode int
- defer func() {
- if opt.alarmObject == nil {
- return
- }
- if opt.alarmVerify != nil && !opt.alarmVerify(body) && err == nil {
- return
- }
- info := &struct {
- TraceID string `json:"trace_id"`
- Request struct {
- Method string `json:"method"`
- URL string `json:"url"`
- } `json:"request"`
- Response struct {
- HTTPCode int `json:"http_code"`
- Body string `json:"body"`
- } `json:"response"`
- Error string `json:"error"`
- }{}
- if opt.trace != nil {
- info.TraceID = opt.trace.ID()
- }
- info.Request.Method = method
- info.Request.URL = url
- info.Response.HTTPCode = httpCode
- info.Response.Body = string(body)
- info.Error = ""
- if err != nil {
- info.Error = fmt.Sprintf("%+v", err)
- }
- raw, _ := json.MarshalIndent(info, "", " ")
- onFailedAlarm(opt.alarmTitle, raw, opt.logger, opt.alarmObject)
- }()
- for k := 0; k < retryTimes; k++ {
- body, httpCode, err = doHTTP(ctx, method, url, nil, opt)
- if shouldRetry(ctx, httpCode) || (opt.retryVerify != nil && opt.retryVerify(body)) {
- time.Sleep(retryDelay)
- continue
- }
- return
- }
- return
- }
- // PostForm post form 请求
- func PostForm(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
- return withFormBody(http.MethodPost, url, form, options...)
- }
- // PostJSON post json 请求
- func PostJSON(url string, raw json.RawMessage, options ...Option) (body []byte, err error) {
- return withJSONBody(http.MethodPost, url, raw, options...)
- }
- // PutForm put form 请求
- func PutForm(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
- return withFormBody(http.MethodPut, url, form, options...)
- }
- // PutJSON put json 请求
- func PutJSON(url string, raw json.RawMessage, options ...Option) (body []byte, err error) {
- return withJSONBody(http.MethodPut, url, raw, options...)
- }
- // PatchFrom patch form 请求
- func PatchFrom(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
- return withFormBody(http.MethodPatch, url, form, options...)
- }
- // PatchJSON patch json 请求
- func PatchJSON(url string, raw json.RawMessage, options ...Option) (body []byte, err error) {
- return withJSONBody(http.MethodPatch, url, raw, options...)
- }
- func withFormBody(method, url string, form httpURL.Values, options ...Option) (body []byte, err error) {
- if url == "" {
- return nil, errors.New("url required")
- }
- if len(form) == 0 {
- return nil, errors.New("form required")
- }
- ts := time.Now()
- opt := getOption()
- defer func() {
- if opt.trace != nil {
- opt.dialog.Success = err == nil
- opt.dialog.CostSeconds = time.Since(ts).Seconds()
- opt.trace.AppendDialog(opt.dialog)
- }
- releaseOption(opt)
- }()
- for _, f := range options {
- f(opt)
- }
- opt.header["Content-Type"] = []string{"application/x-www-form-urlencoded; charset=utf-8"}
- if opt.trace != nil {
- opt.header[trace.Header] = []string{opt.trace.ID()}
- }
- ttl := opt.ttl
- if ttl <= 0 {
- ttl = DefaultTTL
- }
- ctx, cancel := context.WithTimeout(context.Background(), ttl)
- defer cancel()
- formValue := form.Encode()
- if opt.dialog != nil {
- decodedURL, _ := httpURL.QueryUnescape(url)
- opt.dialog.Request = &trace.Request{
- TTL: ttl.String(),
- Method: method,
- DecodedURL: decodedURL,
- Header: opt.header,
- Body: formValue,
- }
- }
- retryTimes := opt.retryTimes
- if retryTimes <= 0 {
- retryTimes = DefaultRetryTimes
- }
- retryDelay := opt.retryDelay
- if retryDelay <= 0 {
- retryDelay = DefaultRetryDelay
- }
- var httpCode int
- defer func() {
- if opt.alarmObject == nil {
- return
- }
- if opt.alarmVerify != nil && !opt.alarmVerify(body) && err == nil {
- return
- }
- info := &struct {
- TraceID string `json:"trace_id"`
- Request struct {
- Method string `json:"method"`
- URL string `json:"url"`
- } `json:"request"`
- Response struct {
- HTTPCode int `json:"http_code"`
- Body string `json:"body"`
- } `json:"response"`
- Error string `json:"error"`
- }{}
- if opt.trace != nil {
- info.TraceID = opt.trace.ID()
- }
- info.Request.Method = method
- info.Request.URL = url
- info.Response.HTTPCode = httpCode
- info.Response.Body = string(body)
- info.Error = ""
- if err != nil {
- info.Error = fmt.Sprintf("%+v", err)
- }
- raw, _ := json.MarshalIndent(info, "", " ")
- onFailedAlarm(opt.alarmTitle, raw, opt.logger, opt.alarmObject)
- }()
- for k := 0; k < retryTimes; k++ {
- body, httpCode, err = doHTTP(ctx, method, url, []byte(formValue), opt)
- if shouldRetry(ctx, httpCode) || (opt.retryVerify != nil && opt.retryVerify(body)) {
- time.Sleep(retryDelay)
- continue
- }
- return
- }
- return
- }
- func withJSONBody(method, url string, raw json.RawMessage, options ...Option) (body []byte, err error) {
- if url == "" {
- return nil, errors.New("url required")
- }
- if len(raw) == 0 {
- return nil, errors.New("raw required")
- }
- ts := time.Now()
- opt := getOption()
- defer func() {
- if opt.trace != nil {
- opt.dialog.Success = err == nil
- opt.dialog.CostSeconds = time.Since(ts).Seconds()
- opt.trace.AppendDialog(opt.dialog)
- }
- releaseOption(opt)
- }()
- for _, f := range options {
- f(opt)
- }
- opt.header["Content-Type"] = []string{"application/json; charset=utf-8"}
- if opt.trace != nil {
- opt.header[trace.Header] = []string{opt.trace.ID()}
- }
- ttl := opt.ttl
- if ttl <= 0 {
- ttl = DefaultTTL
- }
- ctx, cancel := context.WithTimeout(context.Background(), ttl)
- defer cancel()
- if opt.dialog != nil {
- decodedURL, _ := httpURL.QueryUnescape(url)
- opt.dialog.Request = &trace.Request{
- TTL: ttl.String(),
- Method: method,
- DecodedURL: decodedURL,
- Header: opt.header,
- Body: string(raw), // TODO unsafe
- }
- }
- retryTimes := opt.retryTimes
- if retryTimes <= 0 {
- retryTimes = DefaultRetryTimes
- }
- retryDelay := opt.retryDelay
- if retryDelay <= 0 {
- retryDelay = DefaultRetryDelay
- }
- var httpCode int
- defer func() {
- if opt.alarmObject == nil {
- return
- }
- if opt.alarmVerify != nil && !opt.alarmVerify(body) && err == nil {
- return
- }
- info := &struct {
- TraceID string `json:"trace_id"`
- Request struct {
- Method string `json:"method"`
- URL string `json:"url"`
- } `json:"request"`
- Response struct {
- HTTPCode int `json:"http_code"`
- Body string `json:"body"`
- } `json:"response"`
- Error string `json:"error"`
- }{}
- if opt.trace != nil {
- info.TraceID = opt.trace.ID()
- }
- info.Request.Method = method
- info.Request.URL = url
- info.Response.HTTPCode = httpCode
- info.Response.Body = string(body)
- info.Error = ""
- if err != nil {
- info.Error = fmt.Sprintf("%+v", err)
- }
- raw, _ := json.MarshalIndent(info, "", " ")
- onFailedAlarm(opt.alarmTitle, raw, opt.logger, opt.alarmObject)
- }()
- for k := 0; k < retryTimes; k++ {
- body, httpCode, err = doHTTP(ctx, method, url, raw, opt)
- if shouldRetry(ctx, httpCode) || (opt.retryVerify != nil && opt.retryVerify(body)) {
- time.Sleep(retryDelay)
- continue
- }
- return
- }
- return
- }
|