code.gitea.io/gitea@v1.21.7/routers/web/events/events.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package events 5 6 import ( 7 "net/http" 8 "time" 9 10 "code.gitea.io/gitea/modules/context" 11 "code.gitea.io/gitea/modules/eventsource" 12 "code.gitea.io/gitea/modules/graceful" 13 "code.gitea.io/gitea/modules/log" 14 "code.gitea.io/gitea/routers/web/auth" 15 ) 16 17 // Events listens for events 18 func Events(ctx *context.Context) { 19 // FIXME: Need to check if resp is actually a http.Flusher! - how though? 20 21 // Set the headers related to event streaming. 22 ctx.Resp.Header().Set("Content-Type", "text/event-stream") 23 ctx.Resp.Header().Set("Cache-Control", "no-cache") 24 ctx.Resp.Header().Set("Connection", "keep-alive") 25 ctx.Resp.Header().Set("X-Accel-Buffering", "no") 26 ctx.Resp.WriteHeader(http.StatusOK) 27 28 if !ctx.IsSigned { 29 // Return unauthorized status event 30 event := &eventsource.Event{ 31 Name: "close", 32 Data: "unauthorized", 33 } 34 _, _ = event.WriteTo(ctx) 35 ctx.Resp.Flush() 36 return 37 } 38 39 // Listen to connection close and un-register messageChan 40 notify := ctx.Done() 41 ctx.Resp.Flush() 42 43 shutdownCtx := graceful.GetManager().ShutdownContext() 44 45 uid := ctx.Doer.ID 46 47 messageChan := eventsource.GetManager().Register(uid) 48 49 unregister := func() { 50 eventsource.GetManager().Unregister(uid, messageChan) 51 // ensure the messageChan is closed 52 for { 53 _, ok := <-messageChan 54 if !ok { 55 break 56 } 57 } 58 } 59 60 if _, err := ctx.Resp.Write([]byte("\n")); err != nil { 61 log.Error("Unable to write to EventStream: %v", err) 62 unregister() 63 return 64 } 65 66 timer := time.NewTicker(30 * time.Second) 67 68 loop: 69 for { 70 select { 71 case <-timer.C: 72 event := &eventsource.Event{ 73 Name: "ping", 74 } 75 _, err := event.WriteTo(ctx.Resp) 76 if err != nil { 77 log.Error("Unable to write to EventStream for user %s: %v", ctx.Doer.Name, err) 78 go unregister() 79 break loop 80 } 81 ctx.Resp.Flush() 82 case <-notify: 83 go unregister() 84 break loop 85 case <-shutdownCtx.Done(): 86 go unregister() 87 break loop 88 case event, ok := <-messageChan: 89 if !ok { 90 break loop 91 } 92 93 // Handle logout 94 if event.Name == "logout" { 95 if ctx.Session.ID() == event.Data { 96 _, _ = (&eventsource.Event{ 97 Name: "logout", 98 Data: "here", 99 }).WriteTo(ctx.Resp) 100 ctx.Resp.Flush() 101 go unregister() 102 auth.HandleSignOut(ctx) 103 break loop 104 } 105 // Replace the event - we don't want to expose the session ID to the user 106 event = &eventsource.Event{ 107 Name: "logout", 108 Data: "elsewhere", 109 } 110 } 111 112 _, err := event.WriteTo(ctx.Resp) 113 if err != nil { 114 log.Error("Unable to write to EventStream for user %s: %v", ctx.Doer.Name, err) 115 go unregister() 116 break loop 117 } 118 ctx.Resp.Flush() 119 } 120 } 121 timer.Stop() 122 }