http_utils.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package utils
  2. import (
  3. "compress/flate"
  4. "compress/gzip"
  5. "compress/zlib"
  6. "context"
  7. "crypto/tls"
  8. "fmt"
  9. "golang.org/x/net/proxy"
  10. "io"
  11. "io/ioutil"
  12. "net"
  13. "net/http"
  14. "net/url"
  15. "strings"
  16. "time"
  17. )
  18. // Error Code接口
  19. type IErrorCode interface {
  20. // 转换为int
  21. ToInt() int
  22. // 方便与int比较
  23. Equals(errCode int) bool
  24. // 获取错误信息
  25. ErrMsg(args ...interface{}) string
  26. }
  27. //easyjson:json
  28. type Res struct {
  29. Head ResHead `json:"head"`
  30. Data interface{} `json:"data,omitempty"`
  31. }
  32. //easyjson:json
  33. type ResHead struct {
  34. ErrCode int `json:"errcode"`
  35. ErrMsg string `json:"errmsg,omitempty"`
  36. Detail string `json:"-"`
  37. HttpStatus int `json:"-"`
  38. }
  39. func E2(errDetail interface{}, errCode IErrorCode, args ...interface{}) *Res {
  40. errMsg := errCode.ErrMsg(args...)
  41. return E(errCode.ToInt(), errMsg, errDetail)
  42. }
  43. func E(status int, errMsg string, errDetail interface{}) *Res {
  44. var msg, detail string
  45. if nil != errDetail {
  46. detail = strings.TrimSpace(fmt.Sprintf("%s", errDetail))
  47. }
  48. msg = strings.TrimSpace(errMsg)
  49. return &Res{
  50. Head: ResHead{
  51. ErrCode: status,
  52. ErrMsg: msg,
  53. Detail: detail,
  54. HttpStatus: http.StatusInternalServerError,
  55. },
  56. }
  57. }
  58. func R(data interface{}) *Res {
  59. return &Res{
  60. Head: ResHead{
  61. ErrCode: 0,
  62. ErrMsg: "",
  63. HttpStatus: http.StatusOK,
  64. },
  65. Data: data,
  66. }
  67. }
  68. // http请求相关默认配置
  69. const (
  70. DefaultHttpDialTimeout = 20 * time.Second
  71. DefaultHttpKeepAlive = 120 * time.Second
  72. DefaultHttpMaxIdleConns = 1000
  73. DefaultHttpMaxIdleConnsPerHost = 1000
  74. DefaultHttpIdleConnTimeout = 90 * time.Second
  75. DefaultHttpTimeout = 30 * time.Second
  76. )
  77. // http请求配置
  78. type RequestPromise struct {
  79. headers http.Header
  80. encoding Charset
  81. timeout time.Duration
  82. proxy func(*http.Request) (*url.URL, error)
  83. dialContext func(ctx context.Context, network, addr string) (net.Conn, error)
  84. client *http.Client
  85. isSkipTls bool
  86. }
  87. // 返回一个http请求配置对象,默认带上压缩头
  88. func NewRequest() *RequestPromise {
  89. return (&RequestPromise{}).
  90. SetHeader("Accept-Encoding", "gzip, deflate, zlib")
  91. }
  92. // 返回一个http请求配置对象,默认带上压缩头和Content-Type = application/json; charset=utf-8
  93. func JSONRequest() *RequestPromise {
  94. return (&RequestPromise{}).
  95. SetHeader("Accept-Encoding", "gzip, deflate, zlib").
  96. SetHeader("Content-Type", "application/json; charset=utf-8")
  97. }
  98. // 返回一个http请求配置对象,默认带上压缩头和Content-Type = application/xml; charset=utf-8
  99. func XMLRequest() *RequestPromise {
  100. return (&RequestPromise{}).
  101. SetHeader("Accept-Encoding", "gzip, deflate, zlib").
  102. SetHeader("Content-Type", "application/xml; charset=utf-8")
  103. }
  104. // 返回一个采用了连接池和Keepalive配置的http client,可以配合RequestPromise的SetClient函数使用
  105. // 默认不使用它,而是每次请求新建连接
  106. func NewPoolingHttpClient() *http.Client {
  107. return &http.Client{
  108. Transport: &http.Transport{
  109. DialContext: (&net.Dialer{
  110. Timeout: DefaultHttpDialTimeout,
  111. KeepAlive: DefaultHttpKeepAlive,
  112. }).DialContext,
  113. MaxIdleConns: DefaultHttpMaxIdleConns,
  114. MaxIdleConnsPerHost: DefaultHttpMaxIdleConnsPerHost,
  115. IdleConnTimeout: DefaultHttpIdleConnTimeout,
  116. },
  117. Timeout: DefaultHttpTimeout, // 此处设置小于等于零的值,意为不超时
  118. }
  119. }
  120. // 设置https忽略本地证书校验
  121. func (v *RequestPromise) SetSkipTls() *RequestPromise {
  122. v.isSkipTls = true
  123. return v
  124. }
  125. // 设置http header
  126. func (v *RequestPromise) SetHeader(key string, value string) *RequestPromise {
  127. if len(strings.TrimSpace(key)) == 0 {
  128. return v
  129. }
  130. key = strings.TrimSpace(key)
  131. if nil == v.headers {
  132. v.headers = make(http.Header)
  133. }
  134. v.headers.Set(key, value)
  135. return v
  136. }
  137. // 设置http响应的编码,默认utf8
  138. func (v *RequestPromise) SetEncoding(encoding Charset) *RequestPromise {
  139. if encoding == UTF8 {
  140. return v
  141. }
  142. v.encoding = encoding
  143. return v
  144. }
  145. // 设置超时时间,从连接到接收到响应的总时间
  146. // 如果此处不设置则采用http client中设置的超时时间,默认http client超时时间30秒
  147. // 如果此处设置不等于零的值,则覆盖http client中设置的超时时间
  148. // 如果此处设置小于零的值,意为不超时
  149. func (v *RequestPromise) SetTimeout(timeout time.Duration) *RequestPromise {
  150. if timeout == 0 {
  151. return v
  152. }
  153. v.timeout = timeout
  154. return v
  155. }
  156. // 设置http或https代理,默认无代理
  157. func (v *RequestPromise) SetHttpProxy(proxyUri string) *RequestPromise {
  158. if len(strings.TrimSpace(proxyUri)) == 0 {
  159. return v
  160. }
  161. proxyUri = strings.TrimSpace(proxyUri)
  162. uri, err := (&url.URL{}).Parse(proxyUri)
  163. if nil != err {
  164. return v
  165. }
  166. v.proxy = http.ProxyURL(uri)
  167. return v
  168. }
  169. // 设置socket5代理,默认无代理
  170. func (v *RequestPromise) SetSocket5Proxy(proxyUri string) *RequestPromise {
  171. if len(strings.TrimSpace(proxyUri)) == 0 {
  172. return v
  173. }
  174. proxyUri = strings.TrimSpace(proxyUri)
  175. dialer, err := proxy.SOCKS5("tcp", proxyUri, nil, proxy.Direct)
  176. if nil != err {
  177. return v
  178. }
  179. v.dialContext = func(_ context.Context, network string, address string) (net.Conn, error) {
  180. return dialer.Dial(network, address)
  181. }
  182. return v
  183. }
  184. // 设置事先实例化好的http client,默认每次请求会新建一个http client
  185. func (v *RequestPromise) SetClient(client *http.Client) *RequestPromise {
  186. if nil == client {
  187. return v
  188. }
  189. v.client = client
  190. return v
  191. }
  192. // 发起请求并返回响应内容
  193. // FORM方式提交数据请设置Content-Type=application/x-www-form-urlencoded请求头,且io.Reader传url.Values.Encode得到的字符串的reader
  194. func (v *RequestPromise) Call(httpMethod string, targetUri string, data io.Reader) ([]byte, error) {
  195. body, _, err := v.CallResponse(httpMethod, targetUri, data)
  196. return body, err
  197. }
  198. // 发起请求并返回响应内容和响应对象引用
  199. func (v *RequestPromise) CallResponse(httpMethod string, targetUri string, data io.Reader) ([]byte, *http.Response, error) {
  200. targetUri = strings.TrimSpace(targetUri)
  201. if len(targetUri) == 0 {
  202. return nil, nil, nil
  203. }
  204. // http request handle
  205. if len(strings.TrimSpace(httpMethod)) == 0 {
  206. httpMethod = http.MethodGet
  207. } else {
  208. httpMethod = strings.ToUpper(strings.TrimSpace(httpMethod))
  209. }
  210. req, err := http.NewRequest(httpMethod, targetUri, data)
  211. if err != nil {
  212. return nil, nil, err
  213. }
  214. if nil != v.headers {
  215. req.Header = v.headers
  216. }
  217. v.initClient()
  218. // send http request & get http response
  219. resp, err := v.client.Do(req)
  220. if err != nil {
  221. return nil, nil, err
  222. }
  223. var body []byte
  224. body, err = v.readResponseBody(resp)
  225. return body, resp, err
  226. }
  227. func (v *RequestPromise) initClient() {
  228. // http client handle
  229. if nil == v.client { // create new http client instance
  230. v.client = &http.Client{Timeout: DefaultHttpTimeout} // default timeout
  231. }
  232. if v.timeout < 0 {
  233. v.timeout = DefaultHttpTimeout // default timeout
  234. }
  235. if v.timeout > 0 {
  236. v.client.Timeout = v.timeout
  237. }
  238. if v.isSkipTls {
  239. if nil == v.client.Transport {
  240. v.client.Transport = &http.Transport{}
  241. }
  242. transport := (v.client.Transport).(*http.Transport)
  243. transport.TLSClientConfig = &tls.Config{
  244. InsecureSkipVerify: true,
  245. //VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
  246. // return nil
  247. //},
  248. }
  249. }
  250. if nil != v.proxy || nil != v.dialContext {
  251. if nil == v.client.Transport {
  252. v.client.Transport = &http.Transport{}
  253. }
  254. transport := (v.client.Transport).(*http.Transport)
  255. if nil != v.proxy {
  256. transport.Proxy = v.proxy
  257. }
  258. if nil != v.dialContext {
  259. transport.DialContext = v.dialContext
  260. }
  261. }
  262. }
  263. func (v *RequestPromise) readResponseBody(resp *http.Response) ([]byte, error) {
  264. defer func(body io.ReadCloser) {
  265. _ = body.Close()
  266. }(resp.Body)
  267. var reader io.ReadCloser
  268. switch resp.Header.Get("Content-Encoding") {
  269. case "gzip":
  270. reader, _ = gzip.NewReader(resp.Body)
  271. defer func(reader io.Closer) {
  272. _ = reader.Close()
  273. }(reader)
  274. case "deflate":
  275. reader = flate.NewReader(resp.Body)
  276. defer func(reader io.Closer) {
  277. _ = reader.Close()
  278. }(reader)
  279. case "zlib":
  280. reader, _ = zlib.NewReader(resp.Body)
  281. defer func(reader io.Closer) {
  282. _ = reader.Close()
  283. }(reader)
  284. default:
  285. reader = resp.Body
  286. }
  287. body, err := ioutil.ReadAll(reader)
  288. if err != nil {
  289. return nil, err
  290. }
  291. if v.encoding != "" {
  292. body = ConvertToEncodingBytes(body, v.encoding)
  293. } else {
  294. cType := resp.Header.Get("Content-Type")
  295. if cType != "" {
  296. cType = strings.ReplaceAll(strings.ToLower(cType), " ", "")
  297. if strings.Contains(cType, "charset=gbk") {
  298. body = ConvertToEncodingBytes(body, GBK)
  299. } else if strings.Contains(cType, "charset=gb18030") {
  300. body = ConvertToEncodingBytes(body, GB18030)
  301. }
  302. }
  303. }
  304. return body, nil
  305. }
  306. // 获取客户端IP地址
  307. func GetRemoteIP(req *http.Request) string {
  308. remoteAddr := req.RemoteAddr
  309. if ip := req.Header.Get("Remote_addr"); ip != "" {
  310. remoteAddr = ip
  311. } else {
  312. remoteAddr, _, _ = net.SplitHostPort(remoteAddr)
  313. }
  314. if remoteAddr == "::1" {
  315. remoteAddr = "127.0.0.1"
  316. }
  317. return remoteAddr
  318. }