1package http
2
3import (
4 "fmt"
5 "io"
6 nethttp "net/http"
7 "time"
8
9 "github.com/bnema/archup/internal/domain/ports"
10)
11
12// HTTPClient implements the HTTPClient port using the standard library
13type HTTPClient struct {
14 client *nethttp.Client
15}
16
17// NewHTTPClient creates a new HTTP client with a default timeout
18func NewHTTPClient() *HTTPClient {
19 return &HTTPClient{
20 client: &nethttp.Client{
21 Timeout: 30 * time.Second,
22 },
23 }
24}
25
26// NewHTTPClientWithTimeout creates a new HTTP client with a custom timeout
27func NewHTTPClientWithTimeout(timeout time.Duration) *HTTPClient {
28 return &HTTPClient{
29 client: &nethttp.Client{
30 Timeout: timeout,
31 },
32 }
33}
34
35// Get performs an HTTP GET request
36func (hc *HTTPClient) Get(url string) (ports.Response, error) {
37 resp, err := hc.client.Get(url)
38 if err != nil {
39 return nil, fmt.Errorf("GET request failed: %w", err)
40 }
41
42 // Read body into memory
43 body, err := io.ReadAll(resp.Body)
44 if err != nil {
45 if closeErr := resp.Body.Close(); closeErr != nil {
46 return nil, fmt.Errorf("failed to read response body: %w (close error: %v)", err, closeErr)
47 }
48 return nil, fmt.Errorf("failed to read response body: %w", err)
49 }
50
51 return &httpResponse{
52 statusCode: resp.StatusCode,
53 body: body,
54 rawResp: resp,
55 }, nil
56}
57
58// Post performs an HTTP POST request
59func (hc *HTTPClient) Post(url string, contentType string, body []byte) (ports.Response, error) {
60 resp, err := hc.client.Post(url, contentType, nil)
61 if err != nil {
62 return nil, fmt.Errorf("POST request failed: %w", err)
63 }
64
65 // Read body into memory
66 respBody, err := io.ReadAll(resp.Body)
67 if err != nil {
68 if closeErr := resp.Body.Close(); closeErr != nil {
69 return nil, fmt.Errorf("failed to read response body: %w (close error: %v)", err, closeErr)
70 }
71 return nil, fmt.Errorf("failed to read response body: %w", err)
72 }
73
74 return &httpResponse{
75 statusCode: resp.StatusCode,
76 body: respBody,
77 rawResp: resp,
78 }, nil
79}
80
81// httpResponse implements the Response port
82type httpResponse struct {
83 statusCode int
84 body []byte
85 rawResp *nethttp.Response
86}
87
88// StatusCode returns the HTTP status code
89func (hr *httpResponse) StatusCode() int {
90 return hr.statusCode
91}
92
93// Body returns the response body
94func (hr *httpResponse) Body() []byte {
95 return hr.body
96}
97
98// Close closes the response
99func (hr *httpResponse) Close() error {
100 if hr.rawResp != nil {
101 return hr.rawResp.Body.Close()
102 }
103 return nil
104}