diff --git a/internal/streams/api.go b/internal/streams/api.go index c2b93b91..697d8e67 100644 --- a/internal/streams/api.go +++ b/internal/streams/api.go @@ -176,3 +176,7 @@ func apiPreload(w http.ResponseWriter, r *http.Request) { http.Error(w, "", http.StatusMethodNotAllowed) } } + +func apiSchemes(w http.ResponseWriter, r *http.Request) { + api.ResponseJSON(w, SupportedSchemes()) +} diff --git a/internal/streams/api_test.go b/internal/streams/api_test.go new file mode 100644 index 00000000..2cb93d2a --- /dev/null +++ b/internal/streams/api_test.go @@ -0,0 +1,66 @@ +package streams + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/AlexxIT/go2rtc/pkg/core" + "github.com/stretchr/testify/require" +) + +func TestApiSchemes(t *testing.T) { + // Setup: Register some test handlers and redirects + HandleFunc("rtsp", func(url string) (core.Producer, error) { return nil, nil }) + HandleFunc("rtmp", func(url string) (core.Producer, error) { return nil, nil }) + RedirectFunc("http", func(url string) (string, error) { return "", nil }) + + t.Run("GET request returns schemes", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/schemes", nil) + w := httptest.NewRecorder() + + apiSchemes(w, req) + + require.Equal(t, http.StatusOK, w.Code) + require.Equal(t, "application/json", w.Header().Get("Content-Type")) + + var schemes []string + err := json.Unmarshal(w.Body.Bytes(), &schemes) + require.NoError(t, err) + require.NotEmpty(t, schemes) + + // Check that our test schemes are in the response + require.Contains(t, schemes, "rtsp") + require.Contains(t, schemes, "rtmp") + require.Contains(t, schemes, "http") + }) +} + +func TestApiSchemesNoDuplicates(t *testing.T) { + // Setup: Register a scheme in both handlers and redirects + HandleFunc("duplicate", func(url string) (core.Producer, error) { return nil, nil }) + RedirectFunc("duplicate", func(url string) (string, error) { return "", nil }) + + req := httptest.NewRequest("GET", "/api/schemes", nil) + w := httptest.NewRecorder() + + apiSchemes(w, req) + + require.Equal(t, http.StatusOK, w.Code) + + var schemes []string + err := json.Unmarshal(w.Body.Bytes(), &schemes) + require.NoError(t, err) + + // Count occurrences of "duplicate" + count := 0 + for _, scheme := range schemes { + if scheme == "duplicate" { + count++ + } + } + + // Should only appear once + require.Equal(t, 1, count, "scheme 'duplicate' should appear exactly once") +} diff --git a/internal/streams/handlers.go b/internal/streams/handlers.go index 8922bb8d..9433044b 100644 --- a/internal/streams/handlers.go +++ b/internal/streams/handlers.go @@ -16,6 +16,21 @@ func HandleFunc(scheme string, handler Handler) { handlers[scheme] = handler } +func SupportedSchemes() []string { + uniqueKeys := make(map[string]struct{}, len(handlers)+len(redirects)) + for scheme := range handlers { + uniqueKeys[scheme] = struct{}{} + } + for scheme := range redirects { + uniqueKeys[scheme] = struct{}{} + } + resultKeys := make([]string, 0, len(uniqueKeys)) + for key := range uniqueKeys { + resultKeys = append(resultKeys, key) + } + return resultKeys +} + func HasProducer(url string) bool { if i := strings.IndexByte(url, ':'); i > 0 { scheme := url[:i] diff --git a/internal/streams/streams.go b/internal/streams/streams.go index 2bc65486..433f9d36 100644 --- a/internal/streams/streams.go +++ b/internal/streams/streams.go @@ -29,6 +29,7 @@ func Init() { api.HandleFunc("api/streams", apiStreams) api.HandleFunc("api/streams.dot", apiStreamsDOT) api.HandleFunc("api/preload", apiPreload) + api.HandleFunc("api/schemes", apiSchemes) if cfg.Publish == nil && cfg.Preload == nil { return