1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
package eventsource
import (
"log"
"net/http"
"strings"
"sync"
)
type subscription struct {
channel string
lastEventId string
out chan Event
}
type outbound struct {
channels []string
event Event
}
type registration struct {
channel string
repository Repository
}
type Server struct {
AllowCORS bool // Enable all handlers to be accessible from any origin
ReplayAll bool // Replay repository even if there's no Last-Event-Id specified
BufferSize int // How many messages do we let the client get behind before disconnecting
Gzip bool // Enable compression if client can accept it
Logger *log.Logger // Logger is a logger that, when set, will be used for logging debug messages
registrations chan *registration
pub chan *outbound
subs chan *subscription
unregister chan *subscription
quit chan bool
isClosed bool
isClosedMutex sync.RWMutex
}
// Create a new Server ready for handler creation and publishing events
func NewServer() *Server {
srv := &Server{
registrations: make(chan *registration),
pub: make(chan *outbound),
subs: make(chan *subscription),
unregister: make(chan *subscription, 2),
quit: make(chan bool),
BufferSize: 128,
}
go srv.run()
return srv
}
// Stop handling publishing
func (srv *Server) Close() {
srv.quit <- true
srv.markServerClosed()
}
// Create a new handler for serving a specified channel
func (srv *Server) Handler(channel string) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
h := w.Header()
h.Set("Content-Type", "text/event-stream; charset=utf-8")
h.Set("Cache-Control", "no-cache, no-store, must-revalidate")
h.Set("Connection", "keep-alive")
if srv.AllowCORS {
h.Set("Access-Control-Allow-Origin", "*")
}
useGzip := srv.Gzip && strings.Contains(req.Header.Get("Accept-Encoding"), "gzip")
if useGzip {
h.Set("Content-Encoding", "gzip")
}
w.WriteHeader(http.StatusOK)
// If the Handler is still active even though the server is closed, stop here.
// Otherwise the Handler will block while publishing to srv.subs indefinitely.
if srv.isServerClosed() {
return
}
sub := &subscription{
channel: channel,
lastEventId: req.Header.Get("Last-Event-ID"),
out: make(chan Event, srv.BufferSize),
}
srv.subs <- sub
flusher := w.(http.Flusher)
notifier := w.(http.CloseNotifier)
flusher.Flush()
enc := NewEncoder(w, useGzip)
for {
select {
case <-notifier.CloseNotify():
srv.unregister <- sub
return
case ev, ok := <-sub.out:
if !ok {
return
}
if err := enc.Encode(ev); err != nil {
srv.unregister <- sub
if srv.Logger != nil {
srv.Logger.Println(err)
}
return
}
flusher.Flush()
}
}
}
}
// Register the repository to be used for the specified channel
func (srv *Server) Register(channel string, repo Repository) {
srv.registrations <- ®istration{
channel: channel,
repository: repo,
}
}
// Publish an event with the specified id to one or more channels
func (srv *Server) Publish(channels []string, ev Event) {
srv.pub <- &outbound{
channels: channels,
event: ev,
}
}
func replay(repo Repository, sub *subscription) {
for ev := range repo.Replay(sub.channel, sub.lastEventId) {
sub.out <- ev
}
}
func (srv *Server) run() {
subs := make(map[string]map[*subscription]struct{})
repos := make(map[string]Repository)
for {
select {
case reg := <-srv.registrations:
repos[reg.channel] = reg.repository
case sub := <-srv.unregister:
delete(subs[sub.channel], sub)
case pub := <-srv.pub:
for _, c := range pub.channels {
for s := range subs[c] {
select {
case s.out <- pub.event:
default:
srv.unregister <- s
close(s.out)
}
}
}
case sub := <-srv.subs:
if _, ok := subs[sub.channel]; !ok {
subs[sub.channel] = make(map[*subscription]struct{})
}
subs[sub.channel][sub] = struct{}{}
if srv.ReplayAll || len(sub.lastEventId) > 0 {
repo, ok := repos[sub.channel]
if ok {
go replay(repo, sub)
}
}
case <-srv.quit:
for _, sub := range subs {
for s := range sub {
close(s.out)
}
}
return
}
}
}
func (srv *Server) isServerClosed() bool {
srv.isClosedMutex.RLock()
defer srv.isClosedMutex.RUnlock()
return srv.isClosed
}
func (srv *Server) markServerClosed() {
srv.isClosedMutex.Lock()
defer srv.isClosedMutex.Unlock()
srv.isClosed = true
}
|