11package utils
22
33import (
4- "bytes"
5- "context"
6- "encoding/json"
7- "fmt"
8- "io"
9- "net/http"
10- "time"
4+ "bytes"
5+ "context"
6+ "encoding/json"
7+ "fmt"
8+ "io"
9+ "net/http"
10+ "time"
1111)
1212
1313// HTTPClient wraps the standard http.Client with additional functionality
1414type HTTPClient struct {
15- client * http.Client
16- maxRetries int
15+ client * http.Client
16+ maxRetries int
1717}
1818
1919// RequestOptions contains options for making HTTP requests
2020type RequestOptions struct {
21- Headers map [string ]string
22- Query map [string ]string
23- Timeout time.Duration
21+ Headers map [string ]string
22+ Query map [string ]string
23+ Timeout time.Duration
2424}
2525
2626// NewHTTPClient creates a new HTTP client with the specified configuration
2727func NewHTTPClient (timeout time.Duration , maxRetries int ) * HTTPClient {
28- return & HTTPClient {
29- client : & http.Client {
30- Timeout : timeout ,
31- },
32- maxRetries : maxRetries ,
33- }
28+ return & HTTPClient {
29+ client : & http.Client {
30+ Timeout : timeout ,
31+ },
32+ maxRetries : maxRetries ,
33+ }
3434}
3535
3636// DoRequest performs an HTTP request with retries and error handling
3737func (c * HTTPClient ) DoRequest (ctx context.Context , method , url string , body interface {}, opts * RequestOptions ) (* http.Response , error ) {
38- var bodyReader io.Reader
39- if body != nil {
40- jsonBody , err := json .Marshal (body )
41- if err != nil {
42- return nil , fmt .Errorf ("failed to marshal request body: %w" , err )
43- }
44- bodyReader = bytes .NewBuffer (jsonBody )
45- }
46-
47- req , err := http .NewRequestWithContext (ctx , method , url , bodyReader )
48- if err != nil {
49- return nil , fmt .Errorf ("failed to create request: %w" , err )
50- }
51-
52- // Apply options
53- if opts != nil {
54- // Add headers
55- for key , value := range opts .Headers {
56- req .Header .Set (key , value )
57- }
58-
59- // Add query parameters
60- q := req .URL .Query ()
61- for key , value := range opts .Query {
62- q .Add (key , value )
63- }
64- req .URL .RawQuery = q .Encode ()
65- }
66-
67- // Set default headers
68- if req .Header .Get ("Content-Type" ) == "" {
69- req .Header .Set ("Content-Type" , "application/json" )
70- }
71-
72- // Perform request with retries
73- var resp * http.Response
74- var lastErr error
75-
76- for attempt := 0 ; attempt <= c .maxRetries ; attempt ++ {
77- resp , lastErr = c .client .Do (req )
78- if lastErr == nil {
79- break
80- }
81-
82- // Check if context is cancelled
83- if ctx .Err () != nil {
84- return nil , ctx .Err ()
85- }
86-
87- // Wait before retrying
88- if attempt < c .maxRetries {
89- time .Sleep (time .Duration (attempt + 1 ) * time .Second )
90- }
91- }
92-
93- if lastErr != nil {
94- return nil , fmt .Errorf ("request failed after %d retries: %w" , c .maxRetries , lastErr )
95- }
96-
97- return resp , nil
38+ var bodyReader io.Reader
39+ if body != nil {
40+ jsonBody , err := json .Marshal (body )
41+ if err != nil {
42+ return nil , fmt .Errorf ("failed to marshal request body: %w" , err )
43+ }
44+ bodyReader = bytes .NewBuffer (jsonBody )
45+ }
46+
47+ req , err := http .NewRequestWithContext (ctx , method , url , bodyReader )
48+ if err != nil {
49+ return nil , fmt .Errorf ("failed to create request: %w" , err )
50+ }
51+
52+ // Apply options
53+ if opts != nil {
54+ // Add headers
55+ for key , value := range opts .Headers {
56+ req .Header .Set (key , value )
57+ }
58+
59+ // Add query parameters
60+ q := req .URL .Query ()
61+ for key , value := range opts .Query {
62+ q .Add (key , value )
63+ }
64+ req .URL .RawQuery = q .Encode ()
65+ }
66+
67+ // Set default headers
68+ if req .Header .Get ("Content-Type" ) == "" {
69+ req .Header .Set ("Content-Type" , "application/json" )
70+ }
71+
72+ // Perform request with retries
73+ var resp * http.Response
74+ var lastErr error
75+
76+ for attempt := 0 ; attempt <= c .maxRetries ; attempt ++ {
77+ resp , lastErr = c .client .Do (req )
78+ if lastErr == nil {
79+ break
80+ }
81+
82+ // Check if context is cancelled
83+ if ctx .Err () != nil {
84+ return nil , ctx .Err ()
85+ }
86+
87+ // Wait before retrying
88+ if attempt < c .maxRetries {
89+ time .Sleep (time .Duration (attempt + 1 ) * time .Second )
90+ }
91+ }
92+
93+ if lastErr != nil {
94+ return nil , fmt .Errorf ("request failed after %d retries: %w" , c .maxRetries , lastErr )
95+ }
96+
97+ return resp , nil
9898}
9999
100100// DecodeResponse decodes the response body into the provided interface
101101func DecodeResponse (resp * http.Response , v interface {}) error {
102- defer resp .Body .Close ()
102+ defer resp .Body .Close ()
103103
104- if resp .StatusCode >= 400 {
105- body , _ := io .ReadAll (resp .Body )
106- return fmt .Errorf ("request failed with status %d: %s" , resp .StatusCode , string (body ))
107- }
104+ if resp .StatusCode >= 400 {
105+ body , _ := io .ReadAll (resp .Body )
106+ return fmt .Errorf ("request failed with status %d: %s" , resp .StatusCode , string (body ))
107+ }
108108
109- if err := json .NewDecoder (resp .Body ).Decode (v ); err != nil {
110- return fmt .Errorf ("failed to decode response: %w" , err )
111- }
109+ if err := json .NewDecoder (resp .Body ).Decode (v ); err != nil {
110+ return fmt .Errorf ("failed to decode response: %w" , err )
111+ }
112112
113- return nil
113+ return nil
114114}
115-
0 commit comments