mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
96 lines
2.6 KiB
Go
96 lines
2.6 KiB
Go
package health
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// ProbeResult captures the outcome of an HTTP probe against an
|
|
// external endpoint (typically a speaker on the LAN). Reachable is
|
|
// true when the server-side fetch succeeded; otherwise CurlCommand
|
|
// holds a ready-to-paste fallback so the operator can run the same
|
|
// request from a machine that *can* reach the target.
|
|
//
|
|
// Body is only populated when Reachable is true; otherwise Err
|
|
// carries the underlying transport error.
|
|
type ProbeResult struct {
|
|
URL string
|
|
Reachable bool
|
|
Status int
|
|
Body []byte
|
|
Err string
|
|
CurlCommand string
|
|
}
|
|
|
|
// ProbeGet issues an HTTP GET against rawURL using a short timeout.
|
|
// CurlCommand is populated in every result, regardless of success,
|
|
// so the UI can always offer "run this from your LAN" as a fallback
|
|
// or as a copy-the-actual-command affordance.
|
|
//
|
|
// This is intentionally a tiny wrapper around net/http rather than
|
|
// a fully-fledged probe abstraction: the slice is one of "we tried
|
|
// from here, here's what we got" plus "or run this elsewhere".
|
|
//
|
|
// ctx is required; pass context.Background() when no caller context
|
|
// is available.
|
|
func ProbeGet(ctx context.Context, rawURL string, timeout time.Duration) ProbeResult {
|
|
result := ProbeResult{
|
|
URL: rawURL,
|
|
CurlCommand: curlForGet(rawURL),
|
|
}
|
|
|
|
if _, err := url.Parse(rawURL); err != nil {
|
|
result.Err = "invalid url: " + err.Error()
|
|
return result
|
|
}
|
|
|
|
probeCtx, cancel := context.WithTimeout(ctx, timeout)
|
|
defer cancel()
|
|
|
|
req, err := http.NewRequestWithContext(probeCtx, http.MethodGet, rawURL, nil)
|
|
if err != nil {
|
|
result.Err = err.Error()
|
|
return result
|
|
}
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
result.Err = err.Error()
|
|
return result
|
|
}
|
|
|
|
defer func() { _ = resp.Body.Close() }()
|
|
|
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) // 1 MiB cap
|
|
if err != nil {
|
|
result.Status = resp.StatusCode
|
|
result.Err = "read body: " + err.Error()
|
|
|
|
return result
|
|
}
|
|
|
|
result.Reachable = true
|
|
result.Status = resp.StatusCode
|
|
result.Body = body
|
|
|
|
return result
|
|
}
|
|
|
|
// curlForGet renders the curl command the operator would run to
|
|
// reproduce the GET from a different host. Single-quotes the URL
|
|
// so shell metacharacters in query strings don't confuse copy-paste.
|
|
func curlForGet(rawURL string) string {
|
|
if strings.Contains(rawURL, "'") {
|
|
// Extremely rare; fall back to double-quotes when the URL
|
|
// itself contains a single quote.
|
|
return fmt.Sprintf("curl -sS \"%s\"", rawURL)
|
|
}
|
|
|
|
return fmt.Sprintf("curl -sS '%s'", rawURL)
|
|
}
|