123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 |
- package request
- import (
- "bytes"
- "crypto/tls"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "mime/multipart"
- "net/http"
- "net/url"
- "os"
- "reflect"
- "strings"
- "time"
- )
- type Request struct {
- cli *http.Client
- transport *http.Transport
- debug bool
- url string
- method string
- time int64
- timeout time.Duration
- headers map[string]string
- cookies map[string]string
- username string
- password string
- data interface{}
- disableKeepAlives bool
- tlsClientConfig *tls.Config
- jar http.CookieJar
- proxy func(*http.Request) (*url.URL, error)
- checkRedirect func(req *http.Request, via []*http.Request) error
- }
- func (r *Request) DisableKeepAlives(v bool) *Request {
- r.disableKeepAlives = v
- return r
- }
- func (r *Request) Jar(v http.CookieJar) *Request {
- r.jar = v
- return r
- }
- func (r *Request) CheckRedirect(v func(req *http.Request, via []*http.Request) error) *Request {
- r.checkRedirect = v
- return r
- }
- func (r *Request) TLSClient(v *tls.Config) *Request {
- return r.SetTLSClient(v)
- }
- func (r *Request) SetTLSClient(v *tls.Config) *Request {
- r.tlsClientConfig = v
- return r
- }
- func (r *Request) Proxy(v func(*http.Request) (*url.URL, error)) *Request {
- r.proxy = v
- return r
- }
- func (r *Request) Transport(v *http.Transport) *Request {
- r.transport = v
- return r
- }
- // Debug model
- func (r *Request) Debug(v bool) *Request {
- r.debug = v
- return r
- }
- // Get transport
- func (r *Request) getTransport() http.RoundTripper {
- if r.transport == nil {
- return http.DefaultTransport
- }
- r.transport.DisableKeepAlives = r.disableKeepAlives
- if r.tlsClientConfig != nil {
- r.transport.TLSClientConfig = r.tlsClientConfig
- }
- if r.proxy != nil {
- r.transport.Proxy = r.proxy
- }
- return http.RoundTripper(r.transport)
- }
- // Build client
- func (r *Request) buildClient() *http.Client {
- if r.cli == nil {
- r.cli = &http.Client{
- Transport: r.getTransport(),
- Jar: r.jar,
- CheckRedirect: r.checkRedirect,
- Timeout: time.Second * r.timeout,
- }
- }
- return r.cli
- }
- // Set headers
- func (r *Request) SetHeaders(headers map[string]string) *Request {
- if headers != nil || len(headers) > 0 {
- for k, v := range headers {
- r.headers[k] = v
- }
- }
- return r
- }
- // Init headers
- func (r *Request) initHeaders(req *http.Request) {
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- for k, v := range r.headers {
- req.Header.Set(k, v)
- }
- }
- // Set cookies
- func (r *Request) SetCookies(cookies map[string]string) *Request {
- if cookies != nil || len(cookies) > 0 {
- for k, v := range cookies {
- r.cookies[k] = v
- }
- }
- return r
- }
- // Init cookies
- func (r *Request) initCookies(req *http.Request) {
- for k, v := range r.cookies {
- req.AddCookie(&http.Cookie{
- Name: k,
- Value: v,
- })
- }
- }
- // Set basic auth
- func (r *Request) SetBasicAuth(username, password string) *Request {
- r.username = username
- r.password = password
- return r
- }
- func (r *Request) initBasicAuth(req *http.Request) {
- if r.username != "" && r.password != "" {
- req.SetBasicAuth(r.username, r.password)
- }
- }
- // Check application/json
- func (r *Request) isJson() bool {
- if len(r.headers) > 0 {
- for _, v := range r.headers {
- if strings.Contains(strings.ToLower(v), "application/json") {
- return true
- }
- }
- }
- return false
- }
- func (r *Request) JSON() *Request {
- r.SetHeaders(map[string]string{"Content-Type": "application/json"})
- return r
- }
- // Build query data
- func (r *Request) buildBody(d ...interface{}) (io.Reader, error) {
- if r.method == "GET" || r.method == "DELETE" || len(d) == 0 || (len(d) > 0 && d[0] == nil) {
- return nil, nil
- }
- switch d[0].(type) {
- case string:
- return strings.NewReader(d[0].(string)), nil
- case []byte:
- return bytes.NewReader(d[0].([]byte)), nil
- case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
- return bytes.NewReader(IntByte(d[0])), nil
- case *bytes.Reader:
- return d[0].(*bytes.Reader), nil
- case *strings.Reader:
- return d[0].(*strings.Reader), nil
- case *bytes.Buffer:
- return d[0].(*bytes.Buffer), nil
- default:
- if r.isJson() {
- b, err := json.Marshal(d[0])
- if err != nil {
- return nil, err
- }
- return bytes.NewReader(b), nil
- }
- }
- t := reflect.TypeOf(d[0]).String()
- if !strings.Contains(t, "map[string]interface") {
- return nil, errors.New("Unsupported data type.")
- }
- data := make([]string, 0)
- for k, v := range d[0].(map[string]interface{}) {
- if s, ok := v.(string); ok {
- data = append(data, fmt.Sprintf("%s=%v", k, s))
- continue
- }
- b, err := json.Marshal(v)
- if err != nil {
- return nil, err
- }
- data = append(data, fmt.Sprintf("%s=%s", k, string(b)))
- }
- return strings.NewReader(strings.Join(data, "&")), nil
- }
- func (r *Request) SetTimeout(d time.Duration) *Request {
- r.timeout = d
- return r
- }
- // Parse query for GET request
- func parseQuery(url string) ([]string, error) {
- urlList := strings.Split(url, "?")
- if len(urlList) < 2 {
- return make([]string, 0), nil
- }
- query := make([]string, 0)
- for _, val := range strings.Split(urlList[1], "&") {
- v := strings.Split(val, "=")
- if len(v) < 2 {
- return make([]string, 0), errors.New("query parameter error")
- }
- query = append(query, fmt.Sprintf("%s=%s", v[0], v[1]))
- }
- return query, nil
- }
- // Build GET request url
- func buildUrl(url string, data ...interface{}) (string, error) {
- query, err := parseQuery(url)
- if err != nil {
- return url, err
- }
- if len(data) > 0 && data[0] != nil {
- t := reflect.TypeOf(data[0]).String()
- switch t {
- case "map[string]interface {}":
- for k, v := range data[0].(map[string]interface{}) {
- vv := ""
- if reflect.TypeOf(v).String() == "string" {
- vv = v.(string)
- } else {
- b, err := json.Marshal(v)
- if err != nil {
- return url, err
- }
- vv = string(b)
- }
- query = append(query, fmt.Sprintf("%s=%s", k, vv))
- }
- case "string":
- param := data[0].(string)
- if param != "" {
- query = append(query, param)
- }
- default:
- return url, errors.New("Unsupported data type.")
- }
- }
- list := strings.Split(url, "?")
- if len(query) > 0 {
- return fmt.Sprintf("%s?%s", list[0], strings.Join(query, "&")), nil
- }
- return list[0], nil
- }
- func (r *Request) elapsedTime(n int64, resp *Response) {
- end := time.Now().UnixNano() / 1e6
- resp.time = end - n
- }
- func (r *Request) log() {
- if r.debug {
- fmt.Printf("[HttpUtils]\n")
- fmt.Printf("-------------------------------------------------------------------\n")
- fmt.Printf("Request: %s %s\nHeaders: %v\nCookies: %v\nTimeout: %ds\nReqBody: %v\n\n", r.method, r.url, r.headers, r.cookies, r.timeout, r.data)
- //fmt.Printf("-------------------------------------------------------------------\n\n")
- }
- }
- // get is a get http request
- func (r *Request) get(url string, data ...interface{}) (*Response, error) {
- return r.request(http.MethodGet, url, data...)
- }
- // post is a post http request
- func (r *Request) post(url string, data ...interface{}) (*Response, error) {
- return r.request(http.MethodPost, url, data...)
- }
- // put is a put http request
- func (r *Request) put(url string, data ...interface{}) (*Response, error) {
- return r.request(http.MethodPut, url, data...)
- }
- // delete is a delete http request
- func (r *Request) delete(url string, data ...interface{}) (*Response, error) {
- return r.request(http.MethodDelete, url, data...)
- }
- // Upload file
- func (r *Request) upload(url, filename, fileinput string) (*Response, error) {
- return r.sendFile(url, filename, fileinput)
- }
- // Get is a get http request
- func (r *Request) Get(url string, data ...interface{}) (string, error) {
- request, err := r.request(http.MethodGet, strings.TrimSpace(url), data...)
- if err != nil {
- return "", err
- }
- body, err := request.Body()
- if err != nil {
- return "", err
- }
- s := string(body)
- return s, nil
- }
- // Post is a post http request
- func (r *Request) Post(url string, data ...interface{}) (string, error) {
- request, err := r.request(http.MethodPost, strings.TrimSpace(url), data...)
- if err != nil {
- return "", err
- }
- body, err := request.Body()
- if err != nil {
- return "", err
- }
- s := string(body)
- return s, nil
- }
- // Put is a put http request
- func (r *Request) Put(url string, data ...interface{}) (string, error) {
- request, err := r.request(http.MethodPut, strings.TrimSpace(url), data...)
- if err != nil {
- return "", err
- }
- body, err := request.Body()
- if err != nil {
- return "", err
- }
- s := string(body)
- return s, nil
- }
- // Delete is a delete http request
- func (r *Request) Delete(url string, data ...interface{}) (string, error) {
- request, err := r.request(http.MethodDelete, strings.TrimSpace(url), data...)
- if err != nil {
- return "", err
- }
- body, err := request.Body()
- if err != nil {
- return "", err
- }
- s := string(body)
- return s, nil
- }
- // Upload file
- func (r *Request) Upload(url, filename, fileinput string) (string, error) {
- file, err := r.sendFile(strings.TrimSpace(url), filename, fileinput)
- if err != nil {
- return "", err
- }
- body, err := file.Body()
- if err != nil {
- return "", err
- }
- s := string(body)
- return s, nil
- }
- // Send http request
- func (r *Request) request(method, url string, data ...interface{}) (*Response, error) {
- // Build Response
- response := &Response{}
- // Start time
- start := time.Now().UnixNano() / 1e6
- // Count elapsed time
- defer r.elapsedTime(start, response)
- if method == "" || url == "" {
- return nil, errors.New("parameter method and url is required")
- }
- // Debug infomation
- defer r.log()
- r.url = url
- if len(data) > 0 {
- r.data = data[0]
- } else {
- r.data = ""
- }
- var (
- err error
- req *http.Request
- body io.Reader
- )
- r.cli = r.buildClient()
- method = strings.ToUpper(method)
- r.method = method
- if method == "GET" || method == "DELETE" {
- url, err = buildUrl(url, data...)
- if err != nil {
- return nil, err
- }
- r.url = url
- }
- body, err = r.buildBody(data...)
- if err != nil {
- return nil, err
- }
- req, err = http.NewRequest(method, url, body)
- if err != nil {
- return nil, err
- }
- r.initHeaders(req)
- r.initCookies(req)
- r.initBasicAuth(req)
- resp, err := r.cli.Do(req)
- if err != nil {
- return nil, err
- }
- response.url = url
- response.resp = resp
- return response, nil
- }
- // Send file
- func (r *Request) sendFile(url, filename, fileinput string) (*Response, error) {
- if url == "" {
- return nil, errors.New("parameter url is required")
- }
- fileBuffer := &bytes.Buffer{}
- bodyWriter := multipart.NewWriter(fileBuffer)
- fileWriter, er := bodyWriter.CreateFormFile(fileinput, filename)
- if er != nil {
- return nil, er
- }
- f, er := os.Open(filename)
- if er != nil {
- return nil, er
- }
- defer f.Close()
- _, er = io.Copy(fileWriter, f)
- if er != nil {
- return nil, er
- }
- contentType := bodyWriter.FormDataContentType()
- bodyWriter.Close()
- // Build Response
- response := &Response{}
- // Start time
- start := time.Now().UnixNano() / 1e6
- // Count elapsed time
- defer r.elapsedTime(start, response)
- // Debug infomation
- defer r.log()
- r.url = url
- r.data = nil
- var (
- err error
- req *http.Request
- )
- r.cli = r.buildClient()
- r.method = "POST"
- req, err = http.NewRequest(r.method, url, fileBuffer)
- if err != nil {
- return nil, err
- }
- r.initHeaders(req)
- r.initCookies(req)
- r.initBasicAuth(req)
- req.Header.Set("Content-Type", contentType)
- resp, err := r.cli.Do(req)
- if err != nil {
- return nil, err
- }
- response.url = url
- response.resp = resp
- return response, nil
- }
|