func NewHandlerLogEntry(r *http.Request) *log.Entry { remoteAddr := xff.GetRemoteAddr(r) entry := log.WithFields(log.Fields{ "context": "http", "url": r.URL, "remote": remoteAddr, }) var err error entry.Data["request_id"], err = generateID(16) if err != nil { panic("can not generate id for request") } return entry }
func (daemon *SSEDaemon) Ops(w http.ResponseWriter, r *http.Request) { ip := xff.GetRemoteAddr(r) log.Infof("SSE[%s] connection started", ip) if r.Header.Get("Accept") != "text/event-stream" { // Not an event stream request, return a 406 Not Acceptable HTTP error w.WriteHeader(406) return } if !daemon.authenticate(r) { w.WriteHeader(401) return } h := w.Header() h.Set("Server", fmt.Sprintf("oplog/%s", VERSION)) h.Set("Content-Type", "text/event-stream; charset=utf-8") h.Set("Cache-Control", "no-cache, no-store, must-revalidate") h.Set("Connection", "close") h.Set("Access-Control-Allow-Origin", "*") var lastId LastId var err error if r.Header.Get("Last-Event-ID") == "" { // No last id provided, use the very last id of the events collection lastId, err = daemon.ol.LastId() if err != nil { log.Warnf("SSE[%s] can't get last id: %s", ip, err) w.WriteHeader(503) return } } else { if lastId, err = NewLastId(r.Header.Get("Last-Event-ID")); err != nil { log.Warnf("SSE[%s] invalid last id: %s", ip, err) w.WriteHeader(400) return } found, err := daemon.ol.HasId(lastId) if err != nil { log.Warnf("SSE[%s] can't check last id: %s", ip, err) w.WriteHeader(503) return } if !found { log.Debugf("SSE[%s] last id not found, falling back to replication id: %s", ip, lastId.String()) // If the requested event id is not found, fallback to a replication id olid := lastId.(*OperationLastId) lastId = olid.Fallback() } // Backward compat, remove when all oplogc will be updated h.Set("Last-Event-ID", r.Header.Get("Last-Event-ID")) } if lastId != nil { log.Debugf("SSE[%s] using last id: %s", ip, lastId.String()) } types := []string{} if r.URL.Query().Get("types") != "" { types = strings.Split(r.URL.Query().Get("types"), ",") } parents := []string{} if r.URL.Query().Get("parents") != "" { parents = strings.Split(r.URL.Query().Get("parents"), ",") } filter := OpLogFilter{ Types: types, Parents: parents, } flusher := w.(http.Flusher) notifier := w.(http.CloseNotifier) ops := make(chan io.WriterTo) stop := make(chan bool) flusher.Flush() go daemon.ol.Tail(lastId, filter, ops, stop) defer func() { // Stop the oplog tailer stop <- true }() daemon.ol.Stats.Clients.Add(1) daemon.ol.Stats.Connections.Add(1) defer daemon.ol.Stats.Clients.Add(-1) // Messages are buffered and flushed every daemon.FlushInterval to save I/Os ticker := time.NewTicker(daemon.FlushInterval) defer ticker.Stop() var empty int8 for { select { case <-notifier.CloseNotify(): log.Infof("SSE[%s] connection closed", ip) return case op := <-ops: log.Debugf("SSE[%s] sending event", ip) daemon.ol.Stats.EventsSent.Add(1) if _, err := op.WriteTo(w); err != nil { log.Warnf("SSE[%s] write error: %s", ip, err) return } empty = -1 case <-ticker.C: // Flush the buffer at regular interval if empty >= 0 { // Skip if buffer has no data, if empty for too long, send a heartbeat if empty >= daemon.HeartbeatTickerCount { if _, err := w.Write([]byte{':', '\n'}); err != nil { log.Warnf("SSE[%s] write error: %s", ip, err) return } } else { empty++ continue } } empty = 0 log.Debugf("SSE[%s] flushing buffer", ip) flusher.Flush() } } }