Skip to content

http

go
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type MyHttpClient struct {
	client *http.Client
}

var httpClient = &MyHttpClient{
	client: &http.Client{
		Timeout: 5 * time.Second,

		Transport: &http.Transport{
			MaxIdleConns:        100,
			MaxIdleConnsPerHost: 10,
			IdleConnTimeout:     90 * time.Second,
		},
	},
}

func GetHttpClient() *MyHttpClient {
	return httpClient
}

type MyRequest struct {
	Method                   string
	Header                   http.Header
	Body                     any
	Query                    url.Values
	AutoProcessingHttpStatus bool
}

type MyResponse struct {
	StatusCode int
	Header     http.Header
	Body       []byte
}

func (m *MyHttpClient) reqJSON(ctx context.Context, urlStr string, reqStruct *MyRequest) (*MyResponse, error) {
	if reqStruct == nil {
		reqStruct = &MyRequest{}
	}

	// 处理 url
	u, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	// 处理 method
	if reqStruct.Method == "" {
		reqStruct.Method = "GET"
	}
	method := strings.ToUpper(reqStruct.Method)

	if reqStruct.Query != nil && len(reqStruct.Query) > 0 {
		u.RawQuery = reqStruct.Query.Encode()
	}

	// 处理 body
	var body io.Reader
	if method != "GET" && reqStruct.Body != nil {
		jsonStr, err := json.Marshal(reqStruct.Body)
		if err != nil {
			return nil, err
		}
		body = bytes.NewBuffer(jsonStr)
	}

	// 创建请求
	req, err := http.NewRequestWithContext(ctx, method, u.String(), body)
	if err != nil {
		return nil, err
	}

	// 处理 header
	for k, v := range reqStruct.Header {
		for _, vv := range v {
			req.Header.Add(k, vv)
		}
	}
	if body != nil && req.Header.Get("Content-Type") == "" {
		req.Header.Set("Content-Type", "application/json")
	}

	// 发送请求
	start := time.Now()
	resp, err := m.client.Do(req)
	duration := time.Since(start)
	log.Printf("HTTP %s %s status=%d duration=%s err=%v",
		method, u.String(), resp.StatusCode, duration, err)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if reqStruct.AutoProcessingHttpStatus {
		if resp.StatusCode >= 400 {
			return nil, fmt.Errorf("http error: %d, body: %s", resp.StatusCode, string(bodyBytes))
		}
	}

	return &MyResponse{StatusCode: resp.StatusCode, Header: resp.Header, Body: bodyBytes}, nil
}