client.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. package httpclient
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "github.com/spf13/cast"
  8. "net/http"
  9. httpURL "net/url"
  10. "time"
  11. "git.bvbej.com/bvbej/base-golang/pkg/trace"
  12. )
  13. const (
  14. // DefaultTTL 一次http请求最长执行1分钟
  15. DefaultTTL = time.Minute
  16. )
  17. // Get 请求
  18. func Get(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
  19. return withoutBody(http.MethodGet, url, form, options...)
  20. }
  21. // Delete delete 请求
  22. func Delete(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
  23. return withoutBody(http.MethodDelete, url, form, options...)
  24. }
  25. func withoutBody(method, url string, form httpURL.Values, options ...Option) (body []byte, err error) {
  26. if url == "" {
  27. return nil, errors.New("url required")
  28. }
  29. if len(form) > 0 {
  30. if url, err = addFormValuesIntoURL(url, form); err != nil {
  31. return
  32. }
  33. }
  34. ts := time.Now()
  35. opt := getOption()
  36. defer func() {
  37. if opt.trace != nil {
  38. opt.dialog.Success = err == nil
  39. opt.dialog.CostSeconds = time.Since(ts).Seconds()
  40. opt.trace.AppendDialog(opt.dialog)
  41. }
  42. releaseOption(opt)
  43. }()
  44. for _, f := range options {
  45. f(opt)
  46. }
  47. opt.header["Content-Type"] = []string{"application/x-www-form-urlencoded; charset=utf-8"}
  48. if opt.trace != nil {
  49. opt.header[trace.Header] = []string{opt.trace.ID()}
  50. }
  51. ttl := opt.ttl
  52. if ttl <= 0 {
  53. ttl = DefaultTTL
  54. }
  55. ctx, cancel := context.WithTimeout(context.Background(), ttl)
  56. defer cancel()
  57. if opt.dialog != nil {
  58. decodedURL, _ := httpURL.QueryUnescape(url)
  59. opt.dialog.Request = &trace.Request{
  60. TTL: ttl.String(),
  61. Method: method,
  62. DecodedURL: decodedURL,
  63. Header: opt.header,
  64. }
  65. }
  66. retryTimes := opt.retryTimes
  67. if retryTimes <= 0 {
  68. retryTimes = DefaultRetryTimes
  69. }
  70. retryDelay := opt.retryDelay
  71. if retryDelay <= 0 {
  72. retryDelay = DefaultRetryDelay
  73. }
  74. var httpCode int
  75. defer func() {
  76. if opt.alarmObject == nil {
  77. return
  78. }
  79. if opt.alarmVerify != nil && !opt.alarmVerify(body) && err == nil {
  80. return
  81. }
  82. info := &struct {
  83. TraceID string `json:"trace_id"`
  84. Request struct {
  85. Method string `json:"method"`
  86. URL string `json:"url"`
  87. } `json:"request"`
  88. Response struct {
  89. HTTPCode int `json:"http_code"`
  90. Body string `json:"body"`
  91. } `json:"response"`
  92. Error string `json:"error"`
  93. }{}
  94. if opt.trace != nil {
  95. info.TraceID = opt.trace.ID()
  96. }
  97. info.Request.Method = method
  98. info.Request.URL = url
  99. info.Response.HTTPCode = httpCode
  100. info.Response.Body = string(body)
  101. info.Error = ""
  102. if err != nil {
  103. info.Error = fmt.Sprintf("%+v", err)
  104. }
  105. raw, _ := json.MarshalIndent(info, "", " ")
  106. onFailedAlarm(opt.alarmTitle, raw, opt.logger, opt.alarmObject)
  107. }()
  108. for k := 0; k < retryTimes; k++ {
  109. body, httpCode, err = doHTTP(ctx, method, url, nil, opt)
  110. if shouldRetry(ctx, httpCode) || (opt.retryVerify != nil && opt.retryVerify(body)) {
  111. time.Sleep(retryDelay)
  112. continue
  113. }
  114. return
  115. }
  116. return
  117. }
  118. // PostForm post form 请求
  119. func PostForm(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
  120. return withFormBody(http.MethodPost, url, form, options...)
  121. }
  122. // PostJSON post json 请求
  123. func PostJSON(url string, raw json.RawMessage, options ...Option) (body []byte, err error) {
  124. return withJSONBody(http.MethodPost, url, raw, options...)
  125. }
  126. // PutForm put form 请求
  127. func PutForm(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
  128. return withFormBody(http.MethodPut, url, form, options...)
  129. }
  130. // PutJSON put json 请求
  131. func PutJSON(url string, raw json.RawMessage, options ...Option) (body []byte, err error) {
  132. return withJSONBody(http.MethodPut, url, raw, options...)
  133. }
  134. // PatchFrom patch form 请求
  135. func PatchFrom(url string, form httpURL.Values, options ...Option) (body []byte, err error) {
  136. return withFormBody(http.MethodPatch, url, form, options...)
  137. }
  138. // PatchJSON patch json 请求
  139. func PatchJSON(url string, raw json.RawMessage, options ...Option) (body []byte, err error) {
  140. return withJSONBody(http.MethodPatch, url, raw, options...)
  141. }
  142. func withFormBody(method, url string, form httpURL.Values, options ...Option) (body []byte, err error) {
  143. if url == "" {
  144. return nil, errors.New("url required")
  145. }
  146. if len(form) == 0 {
  147. return nil, errors.New("form required")
  148. }
  149. ts := time.Now()
  150. opt := getOption()
  151. defer func() {
  152. if opt.trace != nil {
  153. opt.dialog.Success = err == nil
  154. opt.dialog.CostSeconds = time.Since(ts).Seconds()
  155. opt.trace.AppendDialog(opt.dialog)
  156. }
  157. releaseOption(opt)
  158. }()
  159. for _, f := range options {
  160. f(opt)
  161. }
  162. opt.header["Content-Type"] = []string{"application/x-www-form-urlencoded; charset=utf-8"}
  163. if opt.trace != nil {
  164. opt.header[trace.Header] = []string{opt.trace.ID()}
  165. }
  166. ttl := opt.ttl
  167. if ttl <= 0 {
  168. ttl = DefaultTTL
  169. }
  170. ctx, cancel := context.WithTimeout(context.Background(), ttl)
  171. defer cancel()
  172. formValue := form.Encode()
  173. if opt.dialog != nil {
  174. decodedURL, _ := httpURL.QueryUnescape(url)
  175. opt.dialog.Request = &trace.Request{
  176. TTL: ttl.String(),
  177. Method: method,
  178. DecodedURL: decodedURL,
  179. Header: opt.header,
  180. Body: formValue,
  181. }
  182. }
  183. retryTimes := opt.retryTimes
  184. if retryTimes <= 0 {
  185. retryTimes = DefaultRetryTimes
  186. }
  187. retryDelay := opt.retryDelay
  188. if retryDelay <= 0 {
  189. retryDelay = DefaultRetryDelay
  190. }
  191. var httpCode int
  192. defer func() {
  193. if opt.alarmObject == nil {
  194. return
  195. }
  196. if opt.alarmVerify != nil && !opt.alarmVerify(body) && err == nil {
  197. return
  198. }
  199. info := &struct {
  200. TraceID string `json:"trace_id"`
  201. Request struct {
  202. Method string `json:"method"`
  203. URL string `json:"url"`
  204. } `json:"request"`
  205. Response struct {
  206. HTTPCode int `json:"http_code"`
  207. Body string `json:"body"`
  208. } `json:"response"`
  209. Error string `json:"error"`
  210. }{}
  211. if opt.trace != nil {
  212. info.TraceID = opt.trace.ID()
  213. }
  214. info.Request.Method = method
  215. info.Request.URL = url
  216. info.Response.HTTPCode = httpCode
  217. info.Response.Body = string(body)
  218. info.Error = ""
  219. if err != nil {
  220. info.Error = fmt.Sprintf("%+v", err)
  221. }
  222. raw, _ := json.MarshalIndent(info, "", " ")
  223. onFailedAlarm(opt.alarmTitle, raw, opt.logger, opt.alarmObject)
  224. }()
  225. for k := 0; k < retryTimes; k++ {
  226. body, httpCode, err = doHTTP(ctx, method, url, []byte(formValue), opt)
  227. if shouldRetry(ctx, httpCode) || (opt.retryVerify != nil && opt.retryVerify(body)) {
  228. time.Sleep(retryDelay)
  229. continue
  230. }
  231. return
  232. }
  233. return
  234. }
  235. func withJSONBody(method, url string, raw json.RawMessage, options ...Option) (body []byte, err error) {
  236. if url == "" {
  237. return nil, errors.New("url required")
  238. }
  239. if len(raw) == 0 {
  240. return nil, errors.New("raw required")
  241. }
  242. ts := time.Now()
  243. opt := getOption()
  244. defer func() {
  245. if opt.trace != nil {
  246. opt.dialog.Success = err == nil
  247. opt.dialog.CostSeconds = time.Since(ts).Seconds()
  248. opt.trace.AppendDialog(opt.dialog)
  249. }
  250. releaseOption(opt)
  251. }()
  252. for _, f := range options {
  253. f(opt)
  254. }
  255. opt.header["Content-Type"] = []string{"application/json; charset=utf-8"}
  256. if opt.trace != nil {
  257. opt.header[trace.Header] = []string{opt.trace.ID()}
  258. }
  259. ttl := opt.ttl
  260. if ttl <= 0 {
  261. ttl = DefaultTTL
  262. }
  263. ctx, cancel := context.WithTimeout(context.Background(), ttl)
  264. defer cancel()
  265. if opt.dialog != nil {
  266. decodedURL, _ := httpURL.QueryUnescape(url)
  267. opt.dialog.Request = &trace.Request{
  268. TTL: ttl.String(),
  269. Method: method,
  270. DecodedURL: decodedURL,
  271. Header: opt.header,
  272. Body: cast.ToString(raw),
  273. }
  274. }
  275. retryTimes := opt.retryTimes
  276. if retryTimes <= 0 {
  277. retryTimes = DefaultRetryTimes
  278. }
  279. retryDelay := opt.retryDelay
  280. if retryDelay <= 0 {
  281. retryDelay = DefaultRetryDelay
  282. }
  283. var httpCode int
  284. defer func() {
  285. if opt.alarmObject == nil {
  286. return
  287. }
  288. if opt.alarmVerify != nil && !opt.alarmVerify(body) && err == nil {
  289. return
  290. }
  291. info := &struct {
  292. TraceID string `json:"trace_id"`
  293. Request struct {
  294. Method string `json:"method"`
  295. URL string `json:"url"`
  296. } `json:"request"`
  297. Response struct {
  298. HTTPCode int `json:"http_code"`
  299. Body string `json:"body"`
  300. } `json:"response"`
  301. Error string `json:"error"`
  302. }{}
  303. if opt.trace != nil {
  304. info.TraceID = opt.trace.ID()
  305. }
  306. info.Request.Method = method
  307. info.Request.URL = url
  308. info.Response.HTTPCode = httpCode
  309. info.Response.Body = string(body)
  310. info.Error = ""
  311. if err != nil {
  312. info.Error = fmt.Sprintf("%+v", err)
  313. }
  314. raw, _ := json.MarshalIndent(info, "", " ")
  315. onFailedAlarm(opt.alarmTitle, raw, opt.logger, opt.alarmObject)
  316. }()
  317. for k := 0; k < retryTimes; k++ {
  318. body, httpCode, err = doHTTP(ctx, method, url, raw, opt)
  319. if shouldRetry(ctx, httpCode) || (opt.retryVerify != nil && opt.retryVerify(body)) {
  320. time.Sleep(retryDelay)
  321. continue
  322. }
  323. return
  324. }
  325. return
  326. }