client.go 8.7 KB

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