123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- package utils
- import (
- "compress/flate"
- "compress/gzip"
- "compress/zlib"
- "context"
- "crypto/tls"
- "fmt"
- "golang.org/x/net/proxy"
- "io"
- "io/ioutil"
- "net"
- "net/http"
- "net/url"
- "strings"
- "time"
- )
- // Error Code接口
- type IErrorCode interface {
- // 转换为int
- ToInt() int
- // 方便与int比较
- Equals(errCode int) bool
- // 获取错误信息
- ErrMsg(args ...interface{}) string
- }
- //easyjson:json
- type Res struct {
- Head ResHead `json:"head"`
- Data interface{} `json:"data,omitempty"`
- }
- //easyjson:json
- type ResHead struct {
- ErrCode int `json:"errcode"`
- ErrMsg string `json:"errmsg,omitempty"`
- Detail string `json:"-"`
- HttpStatus int `json:"-"`
- }
- func E2(errDetail interface{}, errCode IErrorCode, args ...interface{}) *Res {
- errMsg := errCode.ErrMsg(args...)
- return E(errCode.ToInt(), errMsg, errDetail)
- }
- func E(status int, errMsg string, errDetail interface{}) *Res {
- var msg, detail string
- if nil != errDetail {
- detail = strings.TrimSpace(fmt.Sprintf("%s", errDetail))
- }
- msg = strings.TrimSpace(errMsg)
- return &Res{
- Head: ResHead{
- ErrCode: status,
- ErrMsg: msg,
- Detail: detail,
- HttpStatus: http.StatusInternalServerError,
- },
- }
- }
- func R(data interface{}) *Res {
- return &Res{
- Head: ResHead{
- ErrCode: 0,
- ErrMsg: "",
- HttpStatus: http.StatusOK,
- },
- Data: data,
- }
- }
- // http请求相关默认配置
- const (
- DefaultHttpDialTimeout = 20 * time.Second
- DefaultHttpKeepAlive = 120 * time.Second
- DefaultHttpMaxIdleConns = 1000
- DefaultHttpMaxIdleConnsPerHost = 1000
- DefaultHttpIdleConnTimeout = 90 * time.Second
- DefaultHttpTimeout = 30 * time.Second
- )
- // http请求配置
- type RequestPromise struct {
- headers http.Header
- encoding Charset
- timeout time.Duration
- proxy func(*http.Request) (*url.URL, error)
- dialContext func(ctx context.Context, network, addr string) (net.Conn, error)
- client *http.Client
- isSkipTls bool
- }
- // 返回一个http请求配置对象,默认带上压缩头
- func NewRequest() *RequestPromise {
- return (&RequestPromise{}).
- SetHeader("Accept-Encoding", "gzip, deflate, zlib")
- }
- // 返回一个http请求配置对象,默认带上压缩头和Content-Type = application/json; charset=utf-8
- func JSONRequest() *RequestPromise {
- return (&RequestPromise{}).
- SetHeader("Accept-Encoding", "gzip, deflate, zlib").
- SetHeader("Content-Type", "application/json; charset=utf-8")
- }
- // 返回一个http请求配置对象,默认带上压缩头和Content-Type = application/xml; charset=utf-8
- func XMLRequest() *RequestPromise {
- return (&RequestPromise{}).
- SetHeader("Accept-Encoding", "gzip, deflate, zlib").
- SetHeader("Content-Type", "application/xml; charset=utf-8")
- }
- // 返回一个采用了连接池和Keepalive配置的http client,可以配合RequestPromise的SetClient函数使用
- // 默认不使用它,而是每次请求新建连接
- func NewPoolingHttpClient() *http.Client {
- return &http.Client{
- Transport: &http.Transport{
- DialContext: (&net.Dialer{
- Timeout: DefaultHttpDialTimeout,
- KeepAlive: DefaultHttpKeepAlive,
- }).DialContext,
- MaxIdleConns: DefaultHttpMaxIdleConns,
- MaxIdleConnsPerHost: DefaultHttpMaxIdleConnsPerHost,
- IdleConnTimeout: DefaultHttpIdleConnTimeout,
- },
- Timeout: DefaultHttpTimeout, // 此处设置小于等于零的值,意为不超时
- }
- }
- // 设置https忽略本地证书校验
- func (v *RequestPromise) SetSkipTls() *RequestPromise {
- v.isSkipTls = true
- return v
- }
- // 设置http header
- func (v *RequestPromise) SetHeader(key string, value string) *RequestPromise {
- if len(strings.TrimSpace(key)) == 0 {
- return v
- }
- key = strings.TrimSpace(key)
- if nil == v.headers {
- v.headers = make(http.Header)
- }
- v.headers.Set(key, value)
- return v
- }
- // 设置http响应的编码,默认utf8
- func (v *RequestPromise) SetEncoding(encoding Charset) *RequestPromise {
- if encoding == UTF8 {
- return v
- }
- v.encoding = encoding
- return v
- }
- // 设置超时时间,从连接到接收到响应的总时间
- // 如果此处不设置则采用http client中设置的超时时间,默认http client超时时间30秒
- // 如果此处设置不等于零的值,则覆盖http client中设置的超时时间
- // 如果此处设置小于零的值,意为不超时
- func (v *RequestPromise) SetTimeout(timeout time.Duration) *RequestPromise {
- if timeout == 0 {
- return v
- }
- v.timeout = timeout
- return v
- }
- // 设置http或https代理,默认无代理
- func (v *RequestPromise) SetHttpProxy(proxyUri string) *RequestPromise {
- if len(strings.TrimSpace(proxyUri)) == 0 {
- return v
- }
- proxyUri = strings.TrimSpace(proxyUri)
- uri, err := (&url.URL{}).Parse(proxyUri)
- if nil != err {
- return v
- }
- v.proxy = http.ProxyURL(uri)
- return v
- }
- // 设置socket5代理,默认无代理
- func (v *RequestPromise) SetSocket5Proxy(proxyUri string) *RequestPromise {
- if len(strings.TrimSpace(proxyUri)) == 0 {
- return v
- }
- proxyUri = strings.TrimSpace(proxyUri)
- dialer, err := proxy.SOCKS5("tcp", proxyUri, nil, proxy.Direct)
- if nil != err {
- return v
- }
- v.dialContext = func(_ context.Context, network string, address string) (net.Conn, error) {
- return dialer.Dial(network, address)
- }
- return v
- }
- // 设置事先实例化好的http client,默认每次请求会新建一个http client
- func (v *RequestPromise) SetClient(client *http.Client) *RequestPromise {
- if nil == client {
- return v
- }
- v.client = client
- return v
- }
- // 发起请求并返回响应内容
- // FORM方式提交数据请设置Content-Type=application/x-www-form-urlencoded请求头,且io.Reader传url.Values.Encode得到的字符串的reader
- func (v *RequestPromise) Call(httpMethod string, targetUri string, data io.Reader) ([]byte, error) {
- body, _, err := v.CallResponse(httpMethod, targetUri, data)
- return body, err
- }
- // 发起请求并返回响应内容和响应对象引用
- func (v *RequestPromise) CallResponse(httpMethod string, targetUri string, data io.Reader) ([]byte, *http.Response, error) {
- targetUri = strings.TrimSpace(targetUri)
- if len(targetUri) == 0 {
- return nil, nil, nil
- }
- // http request handle
- if len(strings.TrimSpace(httpMethod)) == 0 {
- httpMethod = http.MethodGet
- } else {
- httpMethod = strings.ToUpper(strings.TrimSpace(httpMethod))
- }
- req, err := http.NewRequest(httpMethod, targetUri, data)
- if err != nil {
- return nil, nil, err
- }
- if nil != v.headers {
- req.Header = v.headers
- }
- v.initClient()
- // send http request & get http response
- resp, err := v.client.Do(req)
- if err != nil {
- return nil, nil, err
- }
- var body []byte
- body, err = v.readResponseBody(resp)
- return body, resp, err
- }
- func (v *RequestPromise) initClient() {
- // http client handle
- if nil == v.client { // create new http client instance
- v.client = &http.Client{Timeout: DefaultHttpTimeout} // default timeout
- }
- if v.timeout < 0 {
- v.timeout = DefaultHttpTimeout // default timeout
- }
- if v.timeout > 0 {
- v.client.Timeout = v.timeout
- }
- if v.isSkipTls {
- if nil == v.client.Transport {
- v.client.Transport = &http.Transport{}
- }
- transport := (v.client.Transport).(*http.Transport)
- transport.TLSClientConfig = &tls.Config{
- InsecureSkipVerify: true,
- //VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
- // return nil
- //},
- }
- }
- if nil != v.proxy || nil != v.dialContext {
- if nil == v.client.Transport {
- v.client.Transport = &http.Transport{}
- }
- transport := (v.client.Transport).(*http.Transport)
- if nil != v.proxy {
- transport.Proxy = v.proxy
- }
- if nil != v.dialContext {
- transport.DialContext = v.dialContext
- }
- }
- }
- func (v *RequestPromise) readResponseBody(resp *http.Response) ([]byte, error) {
- defer func(body io.ReadCloser) {
- _ = body.Close()
- }(resp.Body)
- var reader io.ReadCloser
- switch resp.Header.Get("Content-Encoding") {
- case "gzip":
- reader, _ = gzip.NewReader(resp.Body)
- defer func(reader io.Closer) {
- _ = reader.Close()
- }(reader)
- case "deflate":
- reader = flate.NewReader(resp.Body)
- defer func(reader io.Closer) {
- _ = reader.Close()
- }(reader)
- case "zlib":
- reader, _ = zlib.NewReader(resp.Body)
- defer func(reader io.Closer) {
- _ = reader.Close()
- }(reader)
- default:
- reader = resp.Body
- }
- body, err := ioutil.ReadAll(reader)
- if err != nil {
- return nil, err
- }
- if v.encoding != "" {
- body = ConvertToEncodingBytes(body, v.encoding)
- } else {
- cType := resp.Header.Get("Content-Type")
- if cType != "" {
- cType = strings.ReplaceAll(strings.ToLower(cType), " ", "")
- if strings.Contains(cType, "charset=gbk") {
- body = ConvertToEncodingBytes(body, GBK)
- } else if strings.Contains(cType, "charset=gb18030") {
- body = ConvertToEncodingBytes(body, GB18030)
- }
- }
- }
- return body, nil
- }
- // 获取客户端IP地址
- func GetRemoteIP(req *http.Request) string {
- remoteAddr := req.RemoteAddr
- if ip := req.Header.Get("Remote_addr"); ip != "" {
- remoteAddr = ip
- } else {
- remoteAddr, _, _ = net.SplitHostPort(remoteAddr)
- }
- if remoteAddr == "::1" {
- remoteAddr = "127.0.0.1"
- }
- return remoteAddr
- }
|