// parseWait is used to parse the ?wait and ?index query params // Returns true on error func parseWait(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool { query := req.URL.Query() if wait := query.Get("wait"); wait != "" { dur, err := time.ParseDuration(wait) if err != nil { resp.WriteHeader(400) resp.Write([]byte("Invalid wait time")) return true } b.MaxQueryTime = dur } if idx := query.Get("index"); idx != "" { index, err := strconv.ParseUint(idx, 10, 64) if err != nil { resp.WriteHeader(400) resp.Write([]byte("Invalid index")) return true } b.MinQueryIndex = index } return false }
// EventList is used to retrieve the recent list of events func (s *HTTPServer) EventList(resp http.ResponseWriter, req *http.Request) (interface{}, error) { // Parse the query options, since we simulate a blocking query var b structs.QueryOptions if parseWait(resp, req, &b) { return nil, nil } // Look for a name filter var nameFilter string if filt := req.URL.Query().Get("name"); filt != "" { nameFilter = filt } // Lots of this logic is borrowed from consul/rpc.go:blockingRPC // However we cannot use that directly since this code has some // slight semantics differences... var timeout <-chan time.Time var notifyCh chan struct{} // Fast path non-blocking if b.MinQueryIndex == 0 { goto RUN_QUERY } // Restrict the max query time if b.MaxQueryTime > maxQueryTime { b.MaxQueryTime = maxQueryTime } // Ensure a time limit is set if we have an index if b.MinQueryIndex > 0 && b.MaxQueryTime == 0 { b.MaxQueryTime = maxQueryTime } // Setup a query timeout if b.MaxQueryTime > 0 { timeout = time.After(b.MaxQueryTime) } // Setup a notification channel for changes SETUP_NOTIFY: if b.MinQueryIndex > 0 { notifyCh = make(chan struct{}, 1) s.agent.eventNotify.Wait(notifyCh) } RUN_QUERY: // Get the recent events events := s.agent.UserEvents() // Filter the events if necessary if nameFilter != "" { for i := 0; i < len(events); i++ { if events[i].Name != nameFilter { events = append(events[:i], events[i+1:]...) i-- } } } // Determine the index var index uint64 if len(events) == 0 { // Return a non-zero index to prevent a hot query loop. This // can be caused by a watch for example when there is no matching // events. index = 1 } else { last := events[len(events)-1] index = uuidToUint64(last.ID) } setIndex(resp, index) // Check for exact match on the query value. Because // the index value is not monotonic, we just ensure it is // not an exact match. if index > 0 && index == b.MinQueryIndex { select { case <-notifyCh: goto SETUP_NOTIFY case <-timeout: } } return events, nil }