gitee.com/quant1x/gox@v1.21.2/http/request.go (about)

     1  package http
     2  
     3  import (
     4  	"bytes"
     5  	"compress/flate"
     6  	"compress/gzip"
     7  	"fmt"
     8  	"gitee.com/quant1x/gox/api"
     9  	"gitee.com/quant1x/gox/exception"
    10  	"gitee.com/quant1x/gox/logger"
    11  	"io"
    12  	"maps"
    13  	"net/http"
    14  	URL "net/url"
    15  	"strings"
    16  	"time"
    17  )
    18  
    19  const (
    20  	MethodGet     = http.MethodGet
    21  	MethodPost    = http.MethodPost
    22  	MethodHead    = http.MethodHead
    23  	MethodPut     = http.MethodPut
    24  	MethodPatch   = http.MethodPatch // RFC 5789
    25  	MethodDelete  = http.MethodDelete
    26  	MethodConnect = http.MethodConnect
    27  	MethodOptions = http.MethodOptions
    28  	MethodTrace   = http.MethodTrace
    29  
    30  	ContentEncoding = "Content-Encoding"
    31  	ContextType     = "Content-Type"
    32  	LastModified    = "Last-Modified"
    33  	IfModifiedSince = "If-Modified-Since"
    34  	charsetUtf8     = "charset=UTF-8"
    35  	ApplicationJson = "application/json" + ";" + charsetUtf8
    36  	ApplicationForm = "application/x-www-form-urlencoded" + ";" + charsetUtf8
    37  )
    38  
    39  var (
    40  	TimeZero = time.Unix(0, 0)
    41  	NotFound = exception.New(http.StatusNotFound, http.StatusText(http.StatusNotFound))
    42  )
    43  
    44  var (
    45  	defaultHeaders = map[string]string{
    46  		"Accept":                    "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    47  		"Accept-Encoding":           "gzip, deflate",
    48  		"Accept-Language":           "zh-CN,zh;q=0.9,en;q=0.8",
    49  		"Cache-Control":             "no-cache",
    50  		"Connection":                "keep-alive",
    51  		"Pragma":                    "no-cache",
    52  		"Upgrade-Insecure-Requests": "1",
    53  		"User-Agent":                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.35",
    54  	}
    55  )
    56  
    57  // HttpRequest HTTP 请求
    58  func HttpRequest(url string, method string, header ...map[string]any) ([]byte, error) {
    59  	data, lastModified, err := Request(url, method, "", header...)
    60  	_ = lastModified
    61  	return data, err
    62  }
    63  
    64  // Get HTTP协议GET请求
    65  func Get(url string, header ...map[string]any) ([]byte, error) {
    66  	data, _, err := Request(url, MethodGet, "", header...)
    67  	return data, err
    68  }
    69  
    70  // Post HTTP协议POST请求
    71  func Post(url string, content string, header ...map[string]any) (data []byte, err error) {
    72  	var requestHeader map[string]any
    73  	if len(header) == 0 {
    74  		requestHeader = make(map[string]any, 0)
    75  	} else {
    76  		requestHeader = header[0]
    77  	}
    78  	requestHeader[ContextType] = ApplicationForm
    79  	content = strings.TrimSpace(content)
    80  	length := len(content)
    81  	if length >= 2 {
    82  		// json 最短长度为2
    83  		start := content[0]
    84  		end := content[length-1]
    85  		if (start == '{' && end == '}') || (start == '[' && end == ']') {
    86  			// 这是json
    87  			requestHeader[ContextType] = ApplicationJson
    88  		}
    89  	}
    90  	data, _, err = Request(url, MethodPost, content, requestHeader)
    91  	return data, err
    92  }
    93  
    94  // Request http request, 支持传入header
    95  func Request(url string, method string, content string, header ...map[string]any) (data []byte, lastModified time.Time, err error) {
    96  	u, err := URL.Parse(url)
    97  	if err != nil {
    98  		return nil, TimeZero, err
    99  	}
   100  	reqHeader := maps.Clone(defaultHeaders)
   101  	reqHeader["Host"] = u.Host
   102  	if len(header) > 0 {
   103  		mapHeader := header[0]
   104  		for k, v := range mapHeader {
   105  			switch val := v.(type) {
   106  			case time.Time:
   107  				val = val.UTC()
   108  				reqHeader[k] = val.Format(time.RFC1123)
   109  			case float32, float64:
   110  				reqHeader[k] = fmt.Sprintf("%f", val)
   111  			case int8, int16, int32, int64:
   112  				reqHeader[k] = fmt.Sprintf("%d", val)
   113  			case uint8, uint16, uint32, uint64:
   114  				reqHeader[k] = fmt.Sprintf("%d", val)
   115  			case string:
   116  				reqHeader[k] = val
   117  			default:
   118  				reqHeader[k] = fmt.Sprintf("%v", val)
   119  			}
   120  		}
   121  	}
   122  
   123  	client := defaultClient()
   124  	var requestBody io.Reader = nil
   125  	if len(content) > 0 {
   126  		requestBody = strings.NewReader(content)
   127  	}
   128  	request, err := http.NewRequest(strings.ToUpper(method), url, requestBody)
   129  	if err != nil {
   130  		return nil, TimeZero, err
   131  	}
   132  	for key, v := range reqHeader {
   133  		request.Header.Add(key, v)
   134  	}
   135  
   136  	response, err := client.Do(request)
   137  	if err != nil {
   138  		return nil, TimeZero, err
   139  	}
   140  	if response.StatusCode == http.StatusNotFound {
   141  		return nil, TimeZero, NotFound
   142  	}
   143  	lm := response.Header.Get(LastModified)
   144  	if response.StatusCode == http.StatusNotModified && !api.IsEmpty(lm) {
   145  		return nil, TimeZero, nil
   146  	}
   147  	lastModified, err = time.Parse(time.RFC1123, lm)
   148  	defer api.CloseQuietly(response.Body)
   149  	body, err := io.ReadAll(response.Body)
   150  	if err != nil {
   151  		return nil, TimeZero, err
   152  	}
   153  	contentEncoding := response.Header.Get(ContentEncoding)
   154  	var reader io.ReadCloser = nil
   155  	if len(contentEncoding) > 0 {
   156  		contentEncoding = strings.ToLower(contentEncoding)
   157  		switch contentEncoding {
   158  		case "gzip":
   159  			reader, err = gzip.NewReader(bytes.NewBuffer(body))
   160  			if err != nil {
   161  				logger.Error(err)
   162  				reader = nil
   163  			}
   164  		case "deflate":
   165  			reader = flate.NewReader(bytes.NewReader(body))
   166  		}
   167  	}
   168  	if reader != nil {
   169  		defer api.CloseQuietly(reader)
   170  		body, err = io.ReadAll(reader)
   171  		if err != nil {
   172  			return nil, TimeZero, err
   173  		}
   174  	}
   175  	return body, lastModified, nil
   176  }