2020-05-08 00:49:00 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package events
import (
"net/http"
"time"
2021-02-19 13:05:35 +03:00
"code.gitea.io/gitea/models"
2020-05-08 00:49:00 +03:00
"code.gitea.io/gitea/modules/context"
2021-02-19 13:05:35 +03:00
"code.gitea.io/gitea/modules/convert"
2020-05-08 00:49:00 +03:00
"code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
2021-02-19 13:05:35 +03:00
"code.gitea.io/gitea/modules/setting"
2020-05-08 00:49:00 +03:00
"code.gitea.io/gitea/routers/user"
2021-03-02 00:08:10 +03:00
jsoniter "github.com/json-iterator/go"
2020-05-08 00:49:00 +03:00
)
// Events listens for events
func Events ( ctx * context . Context ) {
// FIXME: Need to check if resp is actually a http.Flusher! - how though?
// Set the headers related to event streaming.
ctx . Resp . Header ( ) . Set ( "Content-Type" , "text/event-stream" )
ctx . Resp . Header ( ) . Set ( "Cache-Control" , "no-cache" )
ctx . Resp . Header ( ) . Set ( "Connection" , "keep-alive" )
ctx . Resp . Header ( ) . Set ( "X-Accel-Buffering" , "no" )
ctx . Resp . WriteHeader ( http . StatusOK )
2021-03-20 23:39:43 +03:00
if ! ctx . IsSigned {
// Return unauthorized status event
event := ( & eventsource . Event {
2021-04-05 00:37:50 +03:00
Name : "close" ,
Data : "unauthorized" ,
2021-03-20 23:39:43 +03:00
} )
_ , _ = event . WriteTo ( ctx )
ctx . Resp . Flush ( )
return
}
2020-05-08 00:49:00 +03:00
// Listen to connection close and un-register messageChan
notify := ctx . Req . Context ( ) . Done ( )
ctx . Resp . Flush ( )
shutdownCtx := graceful . GetManager ( ) . ShutdownContext ( )
uid := ctx . User . ID
messageChan := eventsource . GetManager ( ) . Register ( uid )
unregister := func ( ) {
eventsource . GetManager ( ) . Unregister ( uid , messageChan )
// ensure the messageChan is closed
for {
_ , ok := <- messageChan
if ! ok {
break
}
}
}
if _ , err := ctx . Resp . Write ( [ ] byte ( "\n" ) ) ; err != nil {
log . Error ( "Unable to write to EventStream: %v" , err )
unregister ( )
return
}
timer := time . NewTicker ( 30 * time . Second )
2021-02-19 13:05:35 +03:00
stopwatchTimer := time . NewTicker ( setting . UI . Notification . MinTimeout )
2020-05-08 00:49:00 +03:00
loop :
for {
select {
case <- timer . C :
event := & eventsource . Event {
Name : "ping" ,
}
_ , err := event . WriteTo ( ctx . Resp )
if err != nil {
log . Error ( "Unable to write to EventStream for user %s: %v" , ctx . User . Name , err )
go unregister ( )
break loop
}
ctx . Resp . Flush ( )
case <- notify :
go unregister ( )
break loop
case <- shutdownCtx . Done ( ) :
go unregister ( )
break loop
2021-02-19 13:05:35 +03:00
case <- stopwatchTimer . C :
sws , err := models . GetUserStopwatches ( ctx . User . ID , models . ListOptions { } )
if err != nil {
log . Error ( "Unable to GetUserStopwatches: %v" , err )
continue
}
apiSWs , err := convert . ToStopWatches ( sws )
if err != nil {
log . Error ( "Unable to APIFormat stopwatches: %v" , err )
continue
}
2021-03-02 00:08:10 +03:00
json := jsoniter . ConfigCompatibleWithStandardLibrary
2021-02-19 13:05:35 +03:00
dataBs , err := json . Marshal ( apiSWs )
if err != nil {
log . Error ( "Unable to marshal stopwatches: %v" , err )
continue
}
_ , err = ( & eventsource . Event {
Name : "stopwatches" ,
Data : string ( dataBs ) ,
} ) . WriteTo ( ctx . Resp )
if err != nil {
log . Error ( "Unable to write to EventStream for user %s: %v" , ctx . User . Name , err )
go unregister ( )
break loop
}
ctx . Resp . Flush ( )
2020-05-08 00:49:00 +03:00
case event , ok := <- messageChan :
if ! ok {
break loop
}
// Handle logout
if event . Name == "logout" {
if ctx . Session . ID ( ) == event . Data {
_ , _ = ( & eventsource . Event {
Name : "logout" ,
Data : "here" ,
} ) . WriteTo ( ctx . Resp )
ctx . Resp . Flush ( )
go unregister ( )
user . HandleSignOut ( ctx )
break loop
}
// Replace the event - we don't want to expose the session ID to the user
event = ( & eventsource . Event {
Name : "logout" ,
Data : "elsewhere" ,
} )
}
_ , err := event . WriteTo ( ctx . Resp )
if err != nil {
log . Error ( "Unable to write to EventStream for user %s: %v" , ctx . User . Name , err )
go unregister ( )
break loop
}
ctx . Resp . Flush ( )
}
}
timer . Stop ( )
}