/* * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package reqcli import ( "bytes" "context" "crypto/tls" "encoding/json" "fmt" "io" "net" "net/http" "sync" "time" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) var ( dftHttpCli *http.Client cliLoadOnce sync.Once ) var httpTimeout = 90 * time.Second func getDefaultTransPort() *http.Transport { return &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 60 * time.Second, DualStack: true, }).DialContext, ForceAttemptHTTP2: true, MaxIdleConns: 100, IdleConnTimeout: 180 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, // nolint: gosec TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } } func GetDefaultHttpClient() *http.Client { cliLoadOnce.Do(func() { dftHttpCli = &http.Client{ Timeout: httpTimeout, Transport: getDefaultTransPort(), } }) return dftHttpCli } func NewHttpCli() (*http.Client, error) { return GetDefaultHttpClient(), nil } func NewHttpCliWithTimeout(timeout time.Duration) (*http.Client, error) { httpCli := &http.Client{ Timeout: timeout, Transport: getDefaultTransPort(), } return httpCli, nil } type JsonRequestBuilder struct { timeout *time.Duration method string url string query map[string]string headers map[string]string payload interface{} result interface{} reqProcessors []func(req *http.Request) } func NewJsonRequestBuilder() *JsonRequestBuilder { builder := JsonRequestBuilder{} return &builder } func (b *JsonRequestBuilder) Timeout(timeout time.Duration) *JsonRequestBuilder { b.timeout = &timeout return b } func (b *JsonRequestBuilder) Method(method string) *JsonRequestBuilder { b.method = method return b } func (b *JsonRequestBuilder) Url(url string) *JsonRequestBuilder { b.url = url return b } func (b *JsonRequestBuilder) Query(query map[string]string) *JsonRequestBuilder { b.query = query return b } func (b *JsonRequestBuilder) Headers(headers map[string]string) *JsonRequestBuilder { b.headers = headers return b } func (b *JsonRequestBuilder) Payload(payload interface{}) *JsonRequestBuilder { b.payload = payload return b } func (b *JsonRequestBuilder) Result(result interface{}) *JsonRequestBuilder { b.result = result return b } func (b *JsonRequestBuilder) ProcessReq(processor func(req *http.Request)) *JsonRequestBuilder { b.reqProcessors = append(b.reqProcessors, processor) return b } func (b *JsonRequestBuilder) Do(ctx context.Context) (statusCode int, err error) { var req *http.Request defer func() { if err != nil { err = errors.Wrapf(err, "DoJsonRequest Error: [%s]%s", b.method, b.url) } }() if b.payload == nil { req, err = http.NewRequestWithContext(ctx, b.method, b.url, nil) } else { switch p := b.payload.(type) { case io.Reader: req, err = http.NewRequestWithContext(ctx, b.method, b.url, p) default: var data []byte data, err = json.Marshal(b.payload) if err != nil { return } req, err = http.NewRequestWithContext(ctx, b.method, b.url, bytes.NewBuffer(data)) } } if err != nil { return } req.Header.Set("Content-Type", "application/json") if b.headers != nil { for k, v := range b.headers { req.Header.Set(k, v) } } for _, reqProcessor := range b.reqProcessors { reqProcessor(req) } q := req.URL.Query() for key, value := range b.query { q.Add(key, value) } req.URL.RawQuery = q.Encode() cli := GetDefaultHttpClient() if b.timeout != nil { cli.Timeout = *b.timeout } logrus.Debugf("http %s %s", b.method, b.url) var resp *http.Response resp, err = cli.Do(req) if err != nil { return } statusCode = resp.StatusCode defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(resp.Body) if err != nil { logrus.Errorf("resp.Body error, %s", err) return } if resp.StatusCode != 200 { msg := fmt.Sprintf("%s %s status=%d, %s", b.method, b.url, resp.StatusCode, body) logrus.Error(msg) err = errors.New(msg) return } if b.result != nil { err = errors.Wrap(json.Unmarshal(body, b.result), "json unmarshal") } return } func DoJsonRequest(ctx context.Context, method string, url string, headers map[string]string, payload, result interface{}) (err error) { _, err = NewJsonRequestBuilder().Method(method).Url(url).Headers(headers).Payload(payload).Result(result).Do(ctx) return }