nandi/oripublic Fork 0
5d476ff5f3dc3b7db3afaccaedc7d9661981449e
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

health.go · 77 lines · 2.5 KBGo Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// Package health probes an ori server's GET /healthz endpoint. It is pure Go
// (no GUI dependency) so the connection logic of the desktop client can be
// unit-tested against an httptest server.
package health

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

// DefaultTimeout bounds a single probe; a desktop connection screen must fail
// fast when the server is down.
const DefaultTimeout = 3 * time.Second

// Report is the outcome of one probe, shaped to cross the Wails bridge as a
// plain JSON object (no error type, so the JavaScript side never has to
// distinguish a rejected promise from a negative answer).
type Report struct {
	// OK is true when /healthz answered with a 2xx status.
	OK bool `json:"ok"`
	// URL is the exact URL that was probed.
	URL string `json:"url"`
	// StatusCode is the HTTP status received, or 0 when no response arrived.
	StatusCode int `json:"statusCode"`
	// Detail is a short human-readable explanation ("ori is reachable",
	// the transport error, or the unexpected status).
	Detail string `json:"detail"`
}

// Check performs GET <baseURL>/healthz. baseURL must already be normalised
// (scheme + host, no trailing slash). A nil client uses http.DefaultTransport
// with DefaultTimeout.
func Check(ctx context.Context, client *http.Client, baseURL string) Report {
	if client == nil {
		client = &http.Client{Timeout: DefaultTimeout}
	}
	target := baseURL + "/healthz"
	rep := Report{URL: target}

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, target, nil)
	if err != nil {
		rep.Detail = fmt.Sprintf("invalid URL: %v", err)
		return rep
	}
	resp, err := client.Do(req)
	if err != nil {
		rep.Detail = fmt.Sprintf("cannot reach ori: %v", err)
		return rep
	}
	defer resp.Body.Close()
	rep.StatusCode = resp.StatusCode

	body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		rep.Detail = fmt.Sprintf("unexpected status %s from /healthz (is this really an ori server?)", resp.Status)
		return rep
	}
	rep.OK = true
	rep.Detail = describe(body)
	return rep
}

// describe turns ori's `{"status":"ok"}` body into a one-liner, tolerating
// any other 2xx body so a future ori version cannot break the client.
func describe(body []byte) string {
	var payload struct {
		Status string `json:"status"`
	}
	if err := json.Unmarshal(body, &payload); err == nil && payload.Status != "" {
		return "ori is reachable (status: " + payload.Status + ")"
	}
	return "ori is reachable"
}