github.heygears.com/openimsdk/tools@v0.0.49/utils/httputil/http_client.go (about) 1 // Copyright © 2024 OpenIM open source community. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package httputil 16 17 import ( 18 "bytes" 19 "context" 20 "encoding/json" 21 "io" 22 "net/http" 23 "time" 24 25 "github.com/openimsdk/tools/errs" 26 ) 27 28 // ClientConfig defines configuration for the HTTP client. 29 type ClientConfig struct { 30 Timeout time.Duration 31 MaxConnsPerHost int 32 } 33 34 // NewClientConfig creates a default client configuration. 35 func NewClientConfig() *ClientConfig { 36 return &ClientConfig{ 37 Timeout: 15 * time.Second, 38 MaxConnsPerHost: 100, 39 } 40 } 41 42 // HTTPClient wraps http.Client and includes additional configuration. 43 type HTTPClient struct { 44 client *http.Client 45 config *ClientConfig 46 } 47 48 // NewHTTPClient creates a new HTTPClient with the provided configuration. 49 func NewHTTPClient(config *ClientConfig) *HTTPClient { 50 return &HTTPClient{ 51 client: &http.Client{ 52 Timeout: config.Timeout, 53 Transport: &http.Transport{ 54 MaxConnsPerHost: config.MaxConnsPerHost, 55 }, 56 }, 57 config: config, 58 } 59 } 60 61 // Get performs a HTTP GET request and returns the response body. 62 func (c *HTTPClient) Get(url string) ([]byte, error) { 63 resp, err := c.client.Get(url) 64 if err != nil { 65 return nil, errs.WrapMsg(err, "GET request failed", "url", url) 66 } 67 defer resp.Body.Close() 68 69 body, err := io.ReadAll(resp.Body) 70 if err != nil { 71 return nil, errs.WrapMsg(err, "failed to read response body", "url", url) 72 } 73 return body, nil 74 } 75 76 // Post sends a JSON-encoded POST request and returns the response body. 77 func (c *HTTPClient) Post(ctx context.Context, url string, headers map[string]string, data any, timeout int) ([]byte, error) { 78 if timeout > 0 { 79 var cancel func() 80 ctx, cancel = context.WithTimeout(ctx, time.Second*time.Duration(timeout)) 81 defer cancel() 82 } 83 body := bytes.NewBuffer(nil) 84 if data != nil { 85 if err := json.NewEncoder(body).Encode(data); err != nil { 86 return nil, errs.WrapMsg(err, "JSON encode failed", "data", data) 87 } 88 } 89 90 req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) 91 if err != nil { 92 return nil, errs.WrapMsg(err, "NewRequestWithContext failed", "url", url, "method", http.MethodPost) 93 } 94 95 for key, value := range headers { 96 req.Header.Set(key, value) 97 } 98 req.Header.Set("Content-Type", "application/json; charset=utf-8") 99 100 resp, err := c.client.Do(req) 101 if err != nil { 102 return nil, errs.WrapMsg(err, "HTTP request failed") 103 } 104 defer resp.Body.Close() 105 106 result, err := io.ReadAll(resp.Body) 107 if err != nil { 108 return nil, errs.WrapMsg(err, "failed to read response body") 109 } 110 111 return result, nil 112 } 113 114 // PostReturn sends a JSON-encoded POST request and decodes the JSON response into output parameter. 115 func (c *HTTPClient) PostReturn(ctx context.Context, url string, headers map[string]string, input, output any, timeout int) error { 116 responseBytes, err := c.Post(ctx, url, headers, input, timeout) 117 if err != nil { 118 return err 119 } 120 if err = json.Unmarshal(responseBytes, output); err != nil { 121 return errs.WrapMsg(err, "JSON unmarshal failed") 122 } 123 return nil 124 }