[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp... #22

Merged
Virgil merged 1 commit from main into dev 2026-04-03 20:32:12 +00:00
2 changed files with 131 additions and 0 deletions

85
http_server.go Normal file
View file

@ -0,0 +1,85 @@
package dns
import (
"context"
"encoding/json"
"net"
"net/http"
"strconv"
"time"
)
// HTTPServer owns the health endpoint listener and server.
type HTTPServer struct {
listener net.Listener
server *http.Server
}
func (server *HTTPServer) Address() string {
if server == nil || server.listener == nil {
return ""
}
return server.listener.Addr().String()
}
func (server *HTTPServer) Close() error {
if server == nil {
return nil
}
shutdownContext, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
if server.server != nil {
if err := server.server.Shutdown(shutdownContext); err != nil {
_ = server.server.Close()
return err
}
return nil
}
if server.listener != nil {
return server.listener.Close()
}
return nil
}
// ServeHTTPHealth starts a minimal HTTP server exposing GET /health.
//
// service.ServeHTTPHealth("127.0.0.1", 5554)
func (service *Service) ServeHTTPHealth(bind string, port int) (*HTTPServer, error) {
if bind == "" {
bind = "127.0.0.1"
}
address := net.JoinHostPort(bind, strconv.Itoa(port))
listener, err := net.Listen("tcp", address)
if err != nil {
return nil, err
}
mux := http.NewServeMux()
mux.HandleFunc("/health", func(responseWriter http.ResponseWriter, request *http.Request) {
if request.Method != http.MethodGet {
responseWriter.WriteHeader(http.StatusMethodNotAllowed)
return
}
responseWriter.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(responseWriter).Encode(service.Health())
})
server := &http.Server{
Handler: mux,
}
httpServer := &HTTPServer{
listener: listener,
server: server,
}
go func() {
_ = server.Serve(listener)
}()
return httpServer, nil
}

View file

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"io"
"net/http"
"net/http/httptest"
"strings"
@ -300,6 +301,51 @@ func TestServiceHealthUsesChainTreeRootAfterDiscovery(t *testing.T) {
}
}
func TestServiceServeHTTPHealthReturnsJSON(t *testing.T) {
service := NewService(ServiceOptions{
Records: map[string]NameRecords{
"gateway.charon.lthn": {
A: []string{"10.10.10.10"},
},
},
})
httpServer, err := service.ServeHTTPHealth("127.0.0.1", 0)
if err != nil {
t.Fatalf("expected health HTTP server to start: %v", err)
}
defer func() {
_ = httpServer.Close()
}()
response, err := http.Get("http://" + httpServer.Address() + "/health")
if err != nil {
t.Fatalf("expected health endpoint to respond: %v", err)
}
defer func() {
_ = response.Body.Close()
}()
if response.StatusCode != http.StatusOK {
t.Fatalf("unexpected health status: %d", response.StatusCode)
}
var payload map[string]any
body, err := io.ReadAll(response.Body)
if err != nil {
t.Fatalf("expected health payload: %v", err)
}
if err := json.Unmarshal(body, &payload); err != nil {
t.Fatalf("expected health JSON: %v", err)
}
if payload["status"] != "ready" {
t.Fatalf("expected ready health status, got %#v", payload["status"])
}
if payload["names_cached"] != float64(1) {
t.Fatalf("expected one cached name, got %#v", payload["names_cached"])
}
}
func TestServiceDiscoverReplacesRecordsFromDiscoverer(t *testing.T) {
records := []map[string]NameRecords{
{