Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
35 changes: 33 additions & 2 deletions internal/api/handlers/v0/list_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,48 @@
"github.com/danielgtaylor/huma/v2"
)

func clientClosedRequest(ctx context.Context, err error) (error, bool) {

Check failure on line 11 in internal/api/handlers/v0/list_errors.go

View workflow job for this annotation

GitHub Actions / Build, Lint, and Validate

error-return: error should be the last type when returning multiple items (revive)
if errors.Is(err, context.Canceled) || errors.Is(ctx.Err(), context.Canceled) {
return huma.NewError(499, "Client closed request", err), true
}
return nil, false
}

// ListServersError maps ListServers failures; client disconnects must not log as 500s.
func ListServersError(ctx context.Context, err error) error {
if err == nil {
return nil
}
if errors.Is(err, context.Canceled) || errors.Is(ctx.Err(), context.Canceled) {
return huma.NewError(499, "Client closed request", err)
if cerr, ok := clientClosedRequest(ctx, err); ok {
return cerr
}
log.Printf("list servers failed: %v", err)
// Do not pass err here: huma serializes extra error args into the response
// body, which would leak internal (e.g. pgx) error detail to clients. Log it
// server-side only, like the sibling handlers in servers.go.
return huma.Error500InternalServerError("Failed to get registry list")
}

// GetServerDetailsError maps get-server-version failures; client disconnects must not log as 500s.
func GetServerDetailsError(ctx context.Context, err error, serverName, version string) error {
if err == nil {
return nil
}
if cerr, ok := clientClosedRequest(ctx, err); ok {
return cerr
}
log.Printf("get server details (%q/%q) failed: %v", serverName, version, err)
return huma.Error500InternalServerError("Failed to get server details")
}

// GetServerVersionsError maps get-server-versions failures; client disconnects must not log as 500s.
func GetServerVersionsError(ctx context.Context, err error, serverName string) error {
if err == nil {
return nil
}
if cerr, ok := clientClosedRequest(ctx, err); ok {
return cerr
}
log.Printf("get server versions (%q) failed: %v", serverName, err)
return huma.Error500InternalServerError("Failed to get server versions")
}
38 changes: 38 additions & 0 deletions internal/api/handlers/v0/list_errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,41 @@ func TestListServersError_realFailureDoesNotLeakDetail(t *testing.T) {
assert.NotContains(t, string(body), "database unavailable")
assert.NotContains(t, string(body), "internal-host")
}

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

err := v0.GetServerDetailsError(ctx, errors.New("query canceled"), "io.github.acme/widget", "1.0.0")
require.Error(t, err)
var se huma.StatusError
require.True(t, errors.As(err, &se))
assert.Equal(t, 499, se.GetStatus())
}

func TestGetServerDetailsError_realFailure(t *testing.T) {
err := v0.GetServerDetailsError(context.Background(), errors.New("database unavailable"), "io.github.acme/widget", "1.0.0")
require.Error(t, err)
var se huma.StatusError
require.True(t, errors.As(err, &se))
assert.Equal(t, 500, se.GetStatus())
}

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

err := v0.GetServerVersionsError(ctx, errors.New("query canceled"), "io.github.acme/widget")
require.Error(t, err)
var se huma.StatusError
require.True(t, errors.As(err, &se))
assert.Equal(t, 499, se.GetStatus())
}

func TestGetServerVersionsError_realFailure(t *testing.T) {
err := v0.GetServerVersionsError(context.Background(), errors.New("database unavailable"), "io.github.acme/widget")
require.Error(t, err)
var se huma.StatusError
require.True(t, errors.As(err, &se))
assert.Equal(t, 500, se.GetStatus())
}
7 changes: 2 additions & 5 deletions internal/api/handlers/v0/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package v0
import (
"context"
"errors"
"log"
"net/http"
"net/url"
"reflect"
Expand Down Expand Up @@ -185,8 +184,7 @@ func RegisterServersEndpoints(api huma.API, pathPrefix string, registry service.
if err.Error() == errRecordNotFound || errors.Is(err, database.ErrNotFound) {
return nil, huma.Error404NotFound("Server not found")
}
log.Printf("get server details (%q/%q) failed: %v", serverName, version, err)
return nil, huma.Error500InternalServerError("Failed to get server details")
return nil, GetServerDetailsError(ctx, err, serverName, version)
}

return &Response[apiv0.ServerResponse]{
Expand Down Expand Up @@ -215,8 +213,7 @@ func RegisterServersEndpoints(api huma.API, pathPrefix string, registry service.
if err.Error() == errRecordNotFound || errors.Is(err, database.ErrNotFound) {
return nil, huma.Error404NotFound("Server not found")
}
log.Printf("get server versions (%q) failed: %v", serverName, err)
return nil, huma.Error500InternalServerError("Failed to get server versions")
return nil, GetServerVersionsError(ctx, err, serverName)
}

// Convert []*ServerResponse to []ServerResponse
Expand Down
Loading