AdGuardHome/internal/stats/http.go
Eugene Burkov 993a3fc42c cherry-pick: 4358 stats races
Merge in DNS/adguard-home from 4358-stats-races to master

Updates #4358

Squashed commit of the following:

commit 162d17b04d95adad21fb9b3c5a6fb64df2e037ec
Merge: 17732cfa d4c3a43b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 17 14:04:20 2022 +0300

    Merge branch 'master' into 4358-stats-races

commit 17732cfa0f3b2589bf2c252697eee1d6b358a66c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 17 13:53:42 2022 +0300

    stats: imp docs, locking

commit 4ee090869af0fa2b777c12027c3b77d5acd6e4de
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 16 20:26:19 2022 +0300

    stats: revert const

commit a7681a1b882cef04511fcd5d569f5abe2f955239
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 16 20:23:00 2022 +0300

    stats: imp concurrency

commit a6c6c1a0572e4201cd24644fd3f86f51fc27f633
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 16 19:51:30 2022 +0300

    stats: imp code, tests, docs

commit 954196b49f5ad91d91f445ff656e63c318e4124c
Merge: 281e00da 6e63757f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 16 13:07:32 2022 +0300

    Merge branch 'master' into 4358-stats-races

commit 281e00daf781d045269584ce0158eed1d77918df
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Aug 12 16:22:18 2022 +0300

    stats: imp closing

commit ed036d9aa7e25498869edfb866b6e923538970eb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Aug 12 16:11:12 2022 +0300

    stats: imp tests more

commit f848a12487ecd2afc8416e800510090cc1be7330
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Aug 12 13:54:19 2022 +0300

    stats: imp tests, code

commit 60e11f042d51ec68850143129e61c701c5e4f3a4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 11 16:36:07 2022 +0300

    stats: fix test

commit 6d97f1db093b5ce0d37984ff96a9ef6f4e02dba1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 11 14:53:21 2022 +0300

    stats: imp code, docs

commit 20c70c2847b0de6c7f9271a8d9a831175ed0c499
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 10 20:53:36 2022 +0300

    stats: imp shared memory safety

commit 8b3945670a190bab070171e6b4976edab1e3e2a2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 10 17:22:55 2022 +0300

    stats: imp code
2022-08-17 18:15:41 +03:00

122 lines
3.7 KiB
Go

// HTTP request handlers for accessing statistics data and configuration settings
package stats
import (
"encoding/json"
"net/http"
"sync/atomic"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/log"
)
// topAddrs is an alias for the types of the TopFoo fields of statsResponse.
// The key is either a client's address or a requested address.
type topAddrs = map[string]uint64
// StatsResp is a response to the GET /control/stats.
type StatsResp struct {
TimeUnits string `json:"time_units"`
TopQueried []topAddrs `json:"top_queried_domains"`
TopClients []topAddrs `json:"top_clients"`
TopBlocked []topAddrs `json:"top_blocked_domains"`
DNSQueries []uint64 `json:"dns_queries"`
BlockedFiltering []uint64 `json:"blocked_filtering"`
ReplacedSafebrowsing []uint64 `json:"replaced_safebrowsing"`
ReplacedParental []uint64 `json:"replaced_parental"`
NumDNSQueries uint64 `json:"num_dns_queries"`
NumBlockedFiltering uint64 `json:"num_blocked_filtering"`
NumReplacedSafebrowsing uint64 `json:"num_replaced_safebrowsing"`
NumReplacedSafesearch uint64 `json:"num_replaced_safesearch"`
NumReplacedParental uint64 `json:"num_replaced_parental"`
AvgProcessingTime float64 `json:"avg_processing_time"`
}
// handleStats handles requests to the GET /control/stats endpoint.
func (s *StatsCtx) handleStats(w http.ResponseWriter, r *http.Request) {
limit := atomic.LoadUint32(&s.limitHours)
start := time.Now()
resp, ok := s.getData(limit)
log.Debug("stats: prepared data in %v", time.Since(start))
if !ok {
// Don't bring the message to the lower case since it's a part of UI
// text for the moment.
aghhttp.Error(r, w, http.StatusInternalServerError, "Couldn't get statistics data")
return
}
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(resp)
if err != nil {
aghhttp.Error(r, w, http.StatusInternalServerError, "json encode: %s", err)
}
}
// configResp is the response to the GET /control/stats_info.
type configResp struct {
IntervalDays uint32 `json:"interval"`
}
// handleStatsInfo handles requests to the GET /control/stats_info endpoint.
func (s *StatsCtx) handleStatsInfo(w http.ResponseWriter, r *http.Request) {
resp := configResp{IntervalDays: atomic.LoadUint32(&s.limitHours) / 24}
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(resp)
if err != nil {
aghhttp.Error(r, w, http.StatusInternalServerError, "json encode: %s", err)
}
}
// handleStatsConfig handles requests to the POST /control/stats_config
// endpoint.
func (s *StatsCtx) handleStatsConfig(w http.ResponseWriter, r *http.Request) {
reqData := configResp{}
err := json.NewDecoder(r.Body).Decode(&reqData)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "json decode: %s", err)
return
}
if !checkInterval(reqData.IntervalDays) {
aghhttp.Error(r, w, http.StatusBadRequest, "Unsupported interval")
return
}
s.setLimit(int(reqData.IntervalDays))
s.configModified()
}
// handleStatsReset handles requests to the POST /control/stats_reset endpoint.
func (s *StatsCtx) handleStatsReset(w http.ResponseWriter, r *http.Request) {
err := s.clear()
if err != nil {
aghhttp.Error(r, w, http.StatusInternalServerError, "stats: %s", err)
}
}
// initWeb registers the handlers for web endpoints of statistics module.
func (s *StatsCtx) initWeb() {
if s.httpRegister == nil {
return
}
s.httpRegister(http.MethodGet, "/control/stats", s.handleStats)
s.httpRegister(http.MethodPost, "/control/stats_reset", s.handleStatsReset)
s.httpRegister(http.MethodPost, "/control/stats_config", s.handleStatsConfig)
s.httpRegister(http.MethodGet, "/control/stats_info", s.handleStatsInfo)
}