package myth import ( "bytes" "compress/gzip" "crypto/tls" "crypto/x509" "encoding/json" "encoding/xml" "errors" "io" "net" "net/http" "time" ) const ( // ReqContentTypeURL application/x-www-form-urlencoded ReqContentTypeURL = `application/x-www-form-urlencoded; charset=utf-8` // ReqContentTypeJSON application/json ReqContentTypeJSON = `application/json; charset=utf-8` // ReqContentTypeXML application/xml ReqContentTypeXML = `application/xml; charset=utf-8` // ReqContentTypeMultipart multipart/form-data ReqContentTypeMultipart = `multipart/form-data` // HTTP header Accept Accept = `Accept` // HTTP header Accept-Encoding AcceptEncoding = `Accept-Encoding` // HTTP header Content-Type ContentType = `Content-Type` // Default Accept-Encoding DefaultAcceptEncoding = `gzip, deflate, br` // ContentTypeJSON application/json ContentTypeJSON = `application/json; charset=utf-8` // ContentTypeURL application/x-www-form-urlencoded ContentTypeURL = `application/x-www-form-urlencoded; charset=utf-8` // ContentTypeURL application/xml ContentTypeXML = `application/xml; charset=utf-8` // RequestTimeOut http request timeout (second) RequestTimeOut = 30 ) const ( // response header Content-Encoding ResContentEncoding = `Content-Encoding` // encoding gzip EncodingGZIP = `gzip` ) var ( reqTimeOut = time.Duration(RequestTimeOut) ErrStausCodeNotOk = errors.New(`StatusCode not 200`) ) // GetRealIP get real IP from Request func GetRealIP(req *http.Request) (ip string) { if ips := req.Header["X-Real-Ip"]; ips != nil { ip = ips[0] } return } // SetHTTPRequestTimeout set request timeout func SetHTTPRequestTimeout(seconds int) { reqTimeOut = time.Duration(seconds) } // HTTPHeader HTTP header type HTTPHeader struct { header map[string]string } // DefaultTTPHeader default HTTP header var DefaultTTPHeader = NewDefaultHTTPHeader() // NewHTTPHeader new HTTPHeader func NewHTTPHeader() (h *HTTPHeader) { return &HTTPHeader{header: map[string]string{}} } // NewDefaultHTTPHeader new HTTPHeader func NewDefaultHTTPHeader() (h *HTTPHeader) { h = NewHTTPHeader() //h.Add(ContentType, ContentTypeJSON) h.Add(AcceptEncoding, DefaultAcceptEncoding) return } // Headers return header func (h *HTTPHeader) Headers() map[string]string { return h.header } // Add add new k-v func (h *HTTPHeader) Add(k, v string) { h.header[k] = v } // Del del k func (h *HTTPHeader) Del(k string) { delete(h.header, k) } // SetContentType set Content-Type func (h *HTTPHeader) SetContentType(v string) { h.header[ContentType] = v } // SetContentTypeJSON set Content-Type application/json func (h *HTTPHeader) SetContentTypeJSON() { h.header[ContentType] = ContentTypeJSON } // SetContentTypeURL set Content-Type application/x-www-form-urlencoded func (h *HTTPHeader) SetContentTypeURL() { h.header[ContentType] = ContentTypeURL } // SetContentTypeXML set Content-Type application/xml func (h *HTTPHeader) SetContentTypeXML() { h.header[ContentType] = ContentTypeXML } // HTTPReqOption request option type HTTPReqOption struct { } // HTTPMessage HTTP response type HTTPMessage struct { StatusCode int Body []byte Header http.Header Req *http.Request Res *http.Response } // Headerx return response http.Header func (m HTTPMessage) Headerx() http.Header { return m.Header } // Request return raw *http.Request func (m HTTPMessage) Request() *http.Request { return m.Req } // Response return raw *http.Response func (m HTTPMessage) Response() *http.Response { return m.Res } // ResponseBytes return Response bytes func (m HTTPMessage) ResponseBytes() []byte { return m.Body } // JSON Body to JSON func (m HTTPMessage) JSON(dest interface{}) (err error) { if m.StatusCode != http.StatusOK { err = ErrStausCodeNotOk return } err = json.Unmarshal(m.Body, &dest) return } // XML Body to XML func (m HTTPMessage) XML(dest interface{}) (err error) { if m.StatusCode != http.StatusOK { err = ErrStausCodeNotOk return } err = xml.Unmarshal(m.Body, &dest) return } // JSONQuery Body to JSONQuery func (m HTTPMessage) JSONQuery() (jq *JSONQuery, err error) { if m.StatusCode != http.StatusOK { err = ErrStausCodeNotOk return } jq, err = NewJSONQuery(m.Body) return } func newRequest(method, uri, certPath, keyPath string, header map[string]string, body io.Reader) (req *http.Request, res *http.Response, err error) { t := &http.Transport{ Dial: func(netw, addr string) (net.Conn, error) { var c net.Conn c, err = net.DialTimeout(netw, addr, time.Second*RequestTimeOut) if err != nil { return nil, err } c.SetDeadline(time.Now().Add(time.Second * reqTimeOut)) return c, nil }, MaxIdleConnsPerHost: 50, ResponseHeaderTimeout: time.Second * reqTimeOut, } if certPath != "" { cert, e := tls.LoadX509KeyPair(certPath, keyPath) if e != nil { t.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} } else { pool := x509.NewCertPool() t.TLSClientConfig = &tls.Config{InsecureSkipVerify: true, Certificates: []tls.Certificate{cert}, RootCAs: pool} } } req, err = http.NewRequest(method, uri, body) if err != nil { return } for k := range header { req.Header.Add(k, header[k]) } client := &http.Client{Transport: t} res, err = client.Do(req) return } func readBody(msg *HTTPMessage, res *http.Response) (err error) { var ( body []byte reader io.Reader ) encoding := res.Header.Get(ResContentEncoding) switch encoding { case EncodingGZIP: reader, err = gzip.NewReader(res.Body) if err == nil { body, err = io.ReadAll(reader) } default: body, err = io.ReadAll(res.Body) } if err != nil { return } msg.StatusCode = res.StatusCode msg.Header = res.Header msg.Body = body return } // Postx request with Content-Type func Postx(contentType, uri string, headers map[string]string, data []byte) (msg HTTPMessage, err error) { headers[ContentType] = contentType if msg.Req, msg.Res, err = newRequest(http.MethodPost, uri, ``, ``, headers, bytes.NewReader(data)); err != nil { return } defer msg.Res.Body.Close() err = readBody(&msg, msg.Res) return } // Post HTTP request POST func Post(uri, certPath, keyPath string, header map[string]string, data io.Reader) (msg HTTPMessage, err error) { if msg.Req, msg.Res, err = newRequest(http.MethodPost, uri, certPath, keyPath, header, data); err != nil { return } defer msg.Res.Body.Close() err = readBody(&msg, msg.Res) return } // Get HTTP request GET func Get(uri, certPath, keyPath string, header map[string]string) (msg HTTPMessage, err error) { //var res *http.Response if msg.Req, msg.Res, err = newRequest(http.MethodGet, uri, certPath, keyPath, header, nil); err != nil { return } defer msg.Res.Body.Close() err = readBody(&msg, msg.Res) return }