package rpc import ( "bytes" "crypto/md5" "crypto/tls" "crypto/x509" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "os" "time" "go.uber.org/zap" "golang.org/x/net/http2" ) var defaultRequest Request // 请求类 type Request struct { host string port uint16 rootCertPath string client *http.Client logger *zap.SugaredLogger } // 发送请求 func (r *Request) Send(methodName string, body any, responseBody any) error { if r.client == nil { return fmt.Errorf("not initialized") } bodyByte, err := json.Marshal(body) if err != nil { return fmt.Errorf("request body json marshal fail: %v", err) } request, err := http.NewRequest("POST", fmt.Sprintf("https://%s:%d/%s", r.host, r.port, methodName), bytes.NewBuffer(bodyByte)) if err != nil { return err } signByte := md5.Sum([]byte(fmt.Sprintf("%s\nsingularity\n%s", methodName, bodyByte))) request.Header.Set("Content-Type", "application/json") request.Header.Set("Authorization", fmt.Sprintf("Digest %s", hex.EncodeToString(signByte[:]))) response, err := r.client.Do(request) if err != nil { return err } defer response.Body.Close() responseBodyByte, err := io.ReadAll(response.Body) if err != nil { return err } if err := json.Unmarshal(responseBodyByte, responseBody); err != nil { return fmt.Errorf("response body json unmarshal fail: %v", err) } if r.logger != nil { r.logger.Infof("method: %s, request: %s, response: %s", methodName, string(bodyByte), string(responseBodyByte), ) } return nil } // 初始化 func Initiate(host string, port uint16, rootCertPath string, logger *zap.SugaredLogger) error { rootCert, err := os.ReadFile(rootCertPath) if err != nil { return fmt.Errorf("cert file read fail") } defaultRequest.host = host defaultRequest.port = port defaultRequest.rootCertPath = rootCertPath defaultRequest.logger = logger certPool := x509.NewCertPool() certPool.AppendCertsFromPEM(rootCert) transport := &http2.Transport{ AllowHTTP: true, TLSClientConfig: &tls.Config{ RootCAs: certPool, ServerName: host, NextProtos: []string{"h2"}, }, StrictMaxConcurrentStreams: true, IdleConnTimeout: time.Minute, PingTimeout: 10 * time.Second, } defaultRequest.client = &http.Client{ Transport: transport, Timeout: 5 * time.Second, } return nil } // 获取请求类 func GetRequest() *Request { return &defaultRequest } func init() { defaultRequest = Request{} }