11package handlers
22
33import (
4+ "context"
45 "errors"
56 "fmt"
67 "log"
78 "net/http"
89 "os"
910 "strings"
11+ "sync"
1012 "time"
1113
1214 "github.com/glimesh/broadcast-box/internal/environment"
1315 "github.com/glimesh/broadcast-box/internal/server/helpers"
1416 "github.com/glimesh/broadcast-box/internal/webrtc/sessions/manager"
17+ "github.com/google/uuid"
1518)
1619
1720func sseHandler (responseWriter http.ResponseWriter , request * http.Request ) {
@@ -34,79 +37,94 @@ func sseHandler(responseWriter http.ResponseWriter, request *http.Request) {
3437 ctx := request .Context ()
3538 responseController := http .NewResponseController (responseWriter )
3639
37- // Setup WHEP/WHIP session for SSE feed
38- sseChannel := getWhipSessionChannel (sessionId )
40+ var writeLock sync.Mutex
41+ writeEvent := func (writeCtx context.Context , msg string ) bool {
42+ if msg == "" || writeCtx .Err () != nil {
43+ return false
44+ }
3945
40- if sseChannel == nil {
41- sseChannel = getWhepSessionChannel (sessionId )
42- }
46+ writeLock .Lock ()
47+ defer writeLock .Unlock ()
4348
44- if sseChannel == nil {
45- helpers .LogHttpError (responseWriter , "Invalid request" , http .StatusBadRequest )
46- return
47- }
49+ if debugSseMessages {
50+ log .Println ("API.SSE Sending:" , msg )
51+ }
4852
49- for {
50- select {
51- case <- ctx .Done ():
52- log .Println ("API.SSE: Client disconnected" )
53- return
53+ if err := responseController .SetWriteDeadline (time .Now ().Add (writeTimeout )); err != nil && ! errors .Is (err , http .ErrNotSupported ) {
54+ log .Println ("API.SSE SetWriteDeadline error:" , err )
55+ return false
56+ }
5457
55- case msg , ok := <- sseChannel :
56- if debugSseMessages {
57- log . Println ( "API.SSE Sending:" , msg )
58- }
58+ _ , err := fmt . Fprintf ( responseWriter , "%s \n " , msg )
59+ if err == nil {
60+ flusher . Flush ( )
61+ }
5962
60- if ! ok || msg == "close" {
61- log .Println ("API.SSE: Channel closed" )
62- return
63- }
63+ if deadlineErr := responseController . SetWriteDeadline (time. Time {}); deadlineErr != nil && ! errors . Is ( deadlineErr , http . ErrNotSupported ) {
64+ log .Println ("API.SSE ClearWriteDeadline error:" , deadlineErr )
65+ return false
66+ }
6467
65- if err := responseController .SetWriteDeadline (time .Now ().Add (writeTimeout )); err != nil && ! errors .Is (err , http .ErrNotSupported ) {
66- log .Println ("API.SSE SetWriteDeadline error:" , err )
67- return
68+ if err != nil {
69+ if errors .Is (err , os .ErrDeadlineExceeded ) {
70+ log .Println ("API.SSE Write timeout" )
71+ } else {
72+ log .Println ("API.SSE Write error:" , err )
6873 }
74+ return false
75+ }
6976
70- _ , err := fmt .Fprintf (responseWriter , "%s\n " , msg )
71- if err == nil {
72- flusher .Flush ()
73- }
77+ return true
78+ }
7479
75- if deadlineErr := responseController .SetWriteDeadline (time.Time {}); deadlineErr != nil && ! errors .Is (deadlineErr , http .ErrNotSupported ) {
76- log .Println ("API.SSE ClearWriteDeadline error:" , deadlineErr )
77- return
78- }
80+ if streamSession , whepSession , foundSession := manager .SessionsManager .GetSessionAndWhepById (sessionId ); foundSession {
81+ subscriberCtx , subscriberCancel := context .WithCancel (ctx )
82+ defer subscriberCancel ()
7983
80- if err != nil {
81- if errors .Is (err , os .ErrDeadlineExceeded ) {
82- log .Println ("API.SSE Write timeout" )
83- } else {
84- log .Println ("API.SSE Write error:" , err )
85- }
86- return
87- }
84+ subscriberID := uuid .NewString ()
85+ subscriberWrite := func (msg string ) bool {
86+ return writeEvent (subscriberCtx , msg )
8887 }
89- }
90- }
88+ if ! whepSession .AddSSESubscriber (subscriberID , subscriberWrite , subscriberCancel ) {
89+ helpers .LogHttpError (responseWriter , "Invalid request" , http .StatusBadRequest )
90+ return
91+ }
92+ defer whepSession .RemoveSSESubscriber (subscriberID )
9193
92- func getWhipSessionChannel ( sessionId string ) chan any {
93- var channel chan any
94- whipSession , ok := manager . SessionsManager . GetHostSessionById ( sessionId )
94+ if ! subscriberWrite ( streamSession . GetSessionStatsEvent ()) {
95+ return
96+ }
9597
96- if ok {
97- channel = whipSession .EventsChannel
98+ host := streamSession .Host .Load ()
99+ if host != nil && ! subscriberWrite (host .GetAvailableLayersEvent ()) {
100+ return
101+ }
102+
103+ <- subscriberCtx .Done ()
104+ log .Println ("API.SSE: Client disconnected" )
105+ return
98106 }
99107
100- return channel
101- }
108+ if streamSession , foundSession := manager .SessionsManager .GetSessionByHostSessionId (sessionId ); foundSession {
109+ if ! writeEvent (ctx , streamSession .GetSessionStatsEvent ()) {
110+ return
111+ }
102112
103- func getWhepSessionChannel (sessionId string ) chan any {
104- var channel chan any
105- whepSession , ok := manager .SessionsManager .GetWhepSessionById (sessionId )
113+ ticker := time .NewTicker (5 * time .Second )
114+ defer ticker .Stop ()
106115
107- if ok {
108- channel = whepSession .SseEventsChannel
116+ for {
117+ select {
118+ case <- ctx .Done ():
119+ log .Println ("API.SSE: Client disconnected" )
120+ return
121+ case <- ticker .C :
122+ if ! writeEvent (ctx , streamSession .GetSessionStatsEvent ()) {
123+ return
124+ }
125+ }
126+ }
109127 }
110128
111- return channel
129+ helpers . LogHttpError ( responseWriter , "Invalid request" , http . StatusBadRequest )
112130}
0 commit comments