Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,16 @@ func NewServer(ctx context.Context, cfg ServerConfig) (*Server, error) {
_, _ = w.Write([]byte("🧰 Hello, World! 🧰"))
})

// healthz endpoint for container orchestration health checks
// (Kubernetes liveness/readiness probes, Docker HEALTHCHECK, etc.).
// Returns 200 OK with a small JSON body so probes can rely on both
// status code and payload.
r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The /healthz endpoint is registered on the main router, which is subject to the hostCheck middleware applied at line 417. In many containerized environments (like Kubernetes), health probes hit the endpoint using the Pod's IP address or localhost. If AllowedHosts is configured with specific domains, these probes will fail with a 403 Forbidden error, potentially causing deployment failures or restart loops. Consider registering health check endpoints on a router group that bypasses host header validation.

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"status":"ok"}`))
})

return s, nil
}

Expand Down
83 changes: 83 additions & 0 deletions internal/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,89 @@ func TestServe(t *testing.T) {
}
}

func TestHealthz(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

addr, port := "127.0.0.1", 5004
cfg := server.ServerConfig{
Version: "0.0.0",
Address: addr,
Port: port,
AllowedHosts: []string{"*"},
}

otelShutdown, err := telemetry.SetupOTel(ctx, "0.0.0", "", false, "toolbox")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
defer func() {
err := otelShutdown(ctx)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
}()

testLogger, err := log.NewStdLogger(os.Stdout, os.Stderr, "info")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
ctx = util.WithLogger(ctx, testLogger)

instrumentation, err := telemetry.CreateTelemetryInstrumentation(cfg.Version)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
ctx = util.WithInstrumentation(ctx, instrumentation)

s, err := server.NewServer(ctx, cfg)
if err != nil {
t.Fatalf("unable to initialize server: %v", err)
}

err = s.Listen(ctx)
if err != nil {
t.Fatalf("unable to start server: %v", err)
}

errCh := make(chan error)
go func() {
defer close(errCh)
err = s.Serve(ctx)
if err != nil {
errCh <- err
}
}()
Comment on lines +165 to +170
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a race condition in this test. The goroutine captures and modifies the err variable from the outer scope (declared at line 154), while the main goroutine reassigns the same variable at line 174 (resp, err := http.Get(url)). You should use a local variable within the goroutine to avoid this race.

	go func() {
		defer close(errCh)
		if err := s.Serve(ctx); err != nil {
			errCh <- err
		}
	}()


url := fmt.Sprintf("http://%s:%d/healthz", addr, port)
resp, err := http.Get(url)
if err != nil {
t.Fatalf("error when sending a request: %s", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status 200, got %d", resp.StatusCode)
}

if ct := resp.Header.Get("Content-Type"); ct != "application/json" {
t.Fatalf("expected Content-Type application/json, got %q", ct)
}

raw, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatalf("error reading from request body: %s", err)
}

var body map[string]string
if err := json.Unmarshal(raw, &body); err != nil {
t.Fatalf("expected JSON body, got %q: %s", string(raw), err)
}
if body["status"] != "ok" {
t.Fatalf(`expected {"status":"ok"}, got %q`, string(raw))
}
}

func TestUpdateServer(t *testing.T) {
ctx, err := testutils.ContextWithNewLogger()
if err != nil {
Expand Down