diff --git a/cmd/list.go b/cmd/list.go index 000c83b..4957d60 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -54,7 +54,7 @@ This command shows your active topics and their count.`, req.Header.Set("Content-Type", "application/json") // Execute the request - resp, err := http.DefaultClient.Do(req) + resp, err := httpClient.Do(req) if err != nil { return fmt.Errorf("HTTP GET request failed: %v", err) } @@ -146,7 +146,7 @@ This command shows your active topics and their count.`, req.Header.Set("Content-Type", "application/json") // Execute the request - resp, err := http.DefaultClient.Do(req) + resp, err := httpClient.Do(req) if err != nil { return fmt.Errorf("HTTP GET request failed: %v", err) } diff --git a/cmd/publish.go b/cmd/publish.go index 34ea667..80f74e4 100644 --- a/cmd/publish.go +++ b/cmd/publish.go @@ -1,6 +1,7 @@ package cmd import ( + "bytes" "context" "crypto/sha256" "encoding/hex" @@ -206,9 +207,9 @@ var publishCmd = &cobra.Command{ } url := baseURL + "/api/v1/publish" - req, err := http.NewRequest("POST", url, strings.NewReader(string(reqBytes))) + req, err := http.NewRequest("POST", url, bytes.NewReader(reqBytes)) if err != nil { - return err + return fmt.Errorf("failed to create publish request: %v", err) } // Only set Authorization header if auth is enabled if !IsAuthDisabled() && token != nil { @@ -216,12 +217,15 @@ var publishCmd = &cobra.Command{ } req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := httpClient.Do(req) if err != nil { return fmt.Errorf("HTTP publish failed: %v", err) } defer resp.Body.Close() //nolint:errcheck - body, _ := io.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read publish response: %v", err) + } if resp.StatusCode != 200 { return fmt.Errorf("publish error: %s", string(body)) } diff --git a/cmd/subscribe.go b/cmd/subscribe.go index 9e7fa66..2d70d31 100644 --- a/cmd/subscribe.go +++ b/cmd/subscribe.go @@ -226,13 +226,16 @@ var subscribeCmd = &cobra.Command{ } req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := httpClient.Do(req) if err != nil { return fmt.Errorf("HTTP POST subscribe failed: %v", err) } defer resp.Body.Close() //nolint:errcheck - body, _ := io.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read subscribe response: %v", err) + } if resp.StatusCode != 200 { return fmt.Errorf("HTTP POST subscribe error: %s", string(body)) @@ -291,7 +294,7 @@ var subscribeCmd = &cobra.Command{ return } req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := httpClient.Do(req) if err != nil { fmt.Printf("Webhook request error: %v\n", err) return @@ -404,7 +407,7 @@ var subscribeCmd = &cobra.Command{ } req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := httpClient.Do(req) if err != nil { fmt.Printf("Webhook request error: %v\n", err) return diff --git a/cmd/utils.go b/cmd/utils.go index e6453ac..f759586 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -1,9 +1,16 @@ package cmd import ( + "net/http" "regexp" + "time" ) +// httpClient is a shared HTTP client with a sensible timeout. +// Use this instead of http.DefaultClient to prevent indefinite hangs +// when the remote server is unreachable or slow to respond. +var httpClient = &http.Client{Timeout: 30 * time.Second} + // extractIPFromURL extracts IP address from URL string func extractIPFromURL(url string) string { ipRegex := regexp.MustCompile(`\d+\.\d+\.\d+\.\d+`)