commit 0aef40100a63a57b54569ec0f07fb0288347acba Author: Cecylia Bocovich cohosh@torproject.org Date: Tue Aug 13 11:44:15 2019 -0400
Implemented handler to fetch broker stats
This implements a handler at https://%5Bsnowflake-broker%5D/metrics for the snowflake collecTor module to fetch stats from the broker. Logged metrics are copied out to the response with a text/plain; charset=utf-8 content type. This implements bug #31376. --- broker/broker.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+)
diff --git a/broker/broker.go b/broker/broker.go index 0a79b8f..259b75a 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -67,6 +67,12 @@ type SnowflakeHandler struct { handle func(*BrokerContext, http.ResponseWriter, *http.Request) }
+// Implements the http.Handler interface +type MetricsHandler struct { + logFilename string + handle func(string, http.ResponseWriter, *http.Request) +} + func (sh SnowflakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Session-ID") @@ -77,6 +83,16 @@ func (sh SnowflakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { sh.handle(sh.BrokerContext, w, r) }
+func (mh MetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Session-ID") + // Return early if it's CORS preflight. + if "OPTIONS" == r.Method { + return + } + mh.handle(mh.logFilename, w, r) +} + // Proxies may poll for client offers concurrently. type ProxyPoll struct { id string @@ -251,6 +267,23 @@ func robotsTxtHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("User-agent: *\nDisallow: /\n")) }
+func metricsHandler(metricsFilename string, w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + + if metricsFilename == "" { + http.NotFound(w, r) + return + } + metricsFile, err := os.OpenFile(metricsFilename, os.O_RDONLY, 0644) + if err != nil { + log.Println("Error opening metrics file for reading") + http.NotFound(w, r) + return + } + + io.Copy(w, metricsFile) +} + func main() { var acmeEmail string var acmeHostnamesCommas string @@ -313,6 +346,7 @@ func main() { http.Handle("/client", SnowflakeHandler{ctx, clientOffers}) http.Handle("/answer", SnowflakeHandler{ctx, proxyAnswers}) http.Handle("/debug", SnowflakeHandler{ctx, debugHandler}) + http.Handle("/metrics", MetricsHandler{metricsFilename, metricsHandler})
server := http.Server{ Addr: addr,