diff --git a/internal/streams/api.go b/internal/streams/api.go index d6142eb0..6a0eb4a9 100644 --- a/internal/streams/api.go +++ b/internal/streams/api.go @@ -177,5 +177,7 @@ func apiPreload(w http.ResponseWriter, r *http.Request) { } func apiSchemes(w http.ResponseWriter, r *http.Request) { + // Wait until all module Init() calls finish in main. + WaitReady() api.ResponseJSON(w, SupportedSchemes()) } diff --git a/internal/streams/api_test.go b/internal/streams/api_test.go index 2cb93d2a..9be08132 100644 --- a/internal/streams/api_test.go +++ b/internal/streams/api_test.go @@ -4,13 +4,17 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "sync" "testing" + "time" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/stretchr/testify/require" ) func TestApiSchemes(t *testing.T) { + SetReady() + // 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 }) @@ -38,6 +42,8 @@ func TestApiSchemes(t *testing.T) { } func TestApiSchemesNoDuplicates(t *testing.T) { + SetReady() + // 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 }) @@ -64,3 +70,46 @@ func TestApiSchemesNoDuplicates(t *testing.T) { // Should only appear once require.Equal(t, 1, count, "scheme 'duplicate' should appear exactly once") } + +func TestApiSchemesWaitsForReady(t *testing.T) { + oldReady := ready + oldReadyOnce := readyOnce + ready = make(chan struct{}) + readyOnce = sync.Once{} + t.Cleanup(func() { + ready = oldReady + readyOnce = oldReadyOnce + }) + + HandleFunc("waittest", func(url string) (core.Producer, error) { return nil, nil }) + + req := httptest.NewRequest("GET", "/api/schemes", nil) + w := httptest.NewRecorder() + done := make(chan struct{}) + + go func() { + apiSchemes(w, req) + close(done) + }() + + select { + case <-done: + t.Fatal("apiSchemes returned before streams became ready") + case <-time.After(50 * time.Millisecond): + } + + SetReady() + + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("apiSchemes did not return after streams became ready") + } + + require.Equal(t, http.StatusOK, w.Code) + + var schemes []string + err := json.Unmarshal(w.Body.Bytes(), &schemes) + require.NoError(t, err) + require.Contains(t, schemes, "waittest") +} diff --git a/internal/streams/ready.go b/internal/streams/ready.go new file mode 100644 index 00000000..951f471f --- /dev/null +++ b/internal/streams/ready.go @@ -0,0 +1,19 @@ +package streams + +import "sync" + +var ( + ready = make(chan struct{}) + readyOnce sync.Once +) + +func SetReady() { + readyOnce.Do(func() { + close(ready) + }) +} + +func WaitReady() { + <-ready +} + diff --git a/main.go b/main.go index 03099862..0ccc231a 100644 --- a/main.go +++ b/main.go @@ -123,5 +123,7 @@ func main() { } } + streams.SetReady() + shell.RunUntilSignal() }