diff --git a/go.mod b/go.mod index b8f947eb..14ffe2e3 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,7 @@ go 1.23.3 require ( github.com/AdguardTeam/dnsproxy v0.73.3 - // TODO(a.garipov): !! Update to tag! - github.com/AdguardTeam/golibs v0.30.3-0.20241108140605-ec84c9f86663 + github.com/AdguardTeam/golibs v0.30.3 github.com/AdguardTeam/urlfilter v0.20.0 github.com/NYTimes/gziphandler v1.1.1 github.com/ameshkov/dnscrypt/v2 v2.3.0 diff --git a/go.sum b/go.sum index 790e3044..cabad9ac 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/AdguardTeam/dnsproxy v0.73.3 h1:aacr6Wu0ed94DDD+gSB6EwF8nvyq0+DAc7oFOgtgUpA= github.com/AdguardTeam/dnsproxy v0.73.3/go.mod h1:18ssqhDgOCiVIwYmmVuXVM05wSwrzkO2yjKhVRWJX/g= -github.com/AdguardTeam/golibs v0.30.3-0.20241108140605-ec84c9f86663 h1:2uhyDq3f4BV48TjXDS0itBU2iwyuWKk1HGLujIJmbd0= -github.com/AdguardTeam/golibs v0.30.3-0.20241108140605-ec84c9f86663/go.mod h1:Ir9dlHfb8nRQsG3Qgo1zoGL+k1qMbcBtb8tcnsvzdAE= +github.com/AdguardTeam/golibs v0.30.3 h1:pRxLjMCJ1cZccjZWMMuKxzQQGEpFbmtyj4Tg7nk5rY0= +github.com/AdguardTeam/golibs v0.30.3/go.mod h1:Ir9dlHfb8nRQsG3Qgo1zoGL+k1qMbcBtb8tcnsvzdAE= github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs= github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= diff --git a/internal/next/cmd/signal.go b/internal/next/cmd/signal.go index 28b2abb7..74928e61 100644 --- a/internal/next/cmd/signal.go +++ b/internal/next/cmd/signal.go @@ -80,9 +80,8 @@ func (h *signalHandler) writePID(ctx context.Context) { return } - // Use 8, since most PIDs will fit. - data := make([]byte, 0, 8) - data = strconv.AppendInt(data, int64(os.Getpid()), 10) + pid := os.Getpid() + data := strconv.AppendInt(nil, int64(pid), 10) data = append(data, '\n') err := aghos.WriteFile(h.pidFile, data, 0o644) @@ -92,7 +91,7 @@ func (h *signalHandler) writePID(ctx context.Context) { return } - h.logger.DebugContext(ctx, "wrote pid", "file", h.pidFile) + h.logger.DebugContext(ctx, "wrote pid", "file", h.pidFile, "pid", pid) } // reconfigure rereads the configuration file and updates and restarts services. diff --git a/internal/next/configmgr/config.go b/internal/next/configmgr/config.go index f5347d80..7b47b147 100644 --- a/internal/next/configmgr/config.go +++ b/internal/next/configmgr/config.go @@ -21,7 +21,7 @@ type config struct { // type check var _ validator = (*config)(nil) -// validate returns an error if the configuration structure is invalid. +// validate implements the [validator] interface for *config. func (c *config) validate() (err error) { if c == nil { return errors.ErrNoValue @@ -65,7 +65,7 @@ type dnsConfig struct { // type check var _ validator = (*dnsConfig)(nil) -// validate returns an error if the DNS configuration structure is invalid. +// validate implements the [validator] interface for *dnsConfig. // // TODO(a.garipov): Add more validations. func (c *dnsConfig) validate() (err error) { @@ -74,7 +74,7 @@ func (c *dnsConfig) validate() (err error) { case c == nil: return errors.ErrNoValue case c.UpstreamTimeout.Duration <= 0: - return newMustBePositiveError("upstream_timeout", c.UpstreamTimeout) + return newErrNotPositive("upstream_timeout", c.UpstreamTimeout) default: return nil } @@ -94,7 +94,7 @@ type httpConfig struct { // type check var _ validator = (*httpConfig)(nil) -// validate returns an error if the HTTP configuration structure is invalid. +// validate implements the [validator] interface for *httpConfig. // // TODO(a.garipov): Add more validations. func (c *httpConfig) validate() (err error) { @@ -102,7 +102,7 @@ func (c *httpConfig) validate() (err error) { case c == nil: return errors.ErrNoValue case c.Timeout.Duration <= 0: - return newMustBePositiveError("timeout", c.Timeout) + return newErrNotPositive("timeout", c.Timeout) default: return c.Pprof.validate() } @@ -114,7 +114,10 @@ type httpPprofConfig struct { Enabled bool `yaml:"enabled"` } -// validate returns an error if the pprof configuration structure is invalid. +// type check +var _ validator = (*httpPprofConfig)(nil) + +// validate implements the [validator] interface for *httpPprofConfig. func (c *httpPprofConfig) validate() (err error) { if c == nil { return errors.ErrNoValue @@ -132,7 +135,7 @@ type logConfig struct { // type check var _ validator = (*logConfig)(nil) -// validate returns an error if the HTTP configuration structure is invalid. +// validate implements the [validator] interface for *logConfig. // // TODO(a.garipov): Add more validations. func (c *logConfig) validate() (err error) { diff --git a/internal/next/configmgr/error.go b/internal/next/configmgr/error.go index dcf82913..4b737197 100644 --- a/internal/next/configmgr/error.go +++ b/internal/next/configmgr/error.go @@ -21,15 +21,11 @@ type numberOrDuration interface { constraints.Integer | timeutil.Duration } -// newMustBePositiveError returns an error about the value that must be positive -// but isn't. prop is the name of the property to mention in the error message. +// newErrNotPositive returns an error about the value that must be positive but +// isn't. prop is the name of the property to mention in the error message. // // TODO(a.garipov): Consider moving such helpers to golibs and use in AdGuardDNS // as well. -func newMustBePositiveError[T numberOrDuration](prop string, v T) (err error) { - if s, ok := any(v).(fmt.Stringer); ok { - return fmt.Errorf("%s: %w, got %s", prop, errors.ErrNotPositive, s) - } - - return fmt.Errorf("%s: %w, got %d", prop, errors.ErrNotPositive, v) +func newErrNotPositive[T numberOrDuration](prop string, v T) (err error) { + return fmt.Errorf("%s: %w, got %v", prop, errors.ErrNotPositive, v) } diff --git a/internal/next/websvc/route.go b/internal/next/websvc/route.go index 1c5580b1..a04e6974 100644 --- a/internal/next/websvc/route.go +++ b/internal/next/websvc/route.go @@ -1,6 +1,11 @@ package websvc -import "net/http" +import ( + "log/slog" + "net/http" + + "github.com/AdguardTeam/golibs/netutil/httputil" +) // Path pattern constants. const ( @@ -21,3 +26,48 @@ const ( routePatternPatchV1SettingsDNS = http.MethodPatch + " " + PathPatternV1SettingsDNS routePatternPatchV1SettingsHTTP = http.MethodPatch + " " + PathPatternV1SettingsHTTP ) + +// route registers all necessary handlers in mux. +func (svc *Service) route(mux *http.ServeMux) { + routes := []struct { + handler http.HandlerFunc + pattern string + isJSON bool + }{{ + handler: svc.handleGetHealthCheck, + pattern: routePatternHealthCheck, + isJSON: false, + }, { + handler: http.FileServer(http.FS(svc.frontend)).ServeHTTP, + pattern: routePatternFrontend, + isJSON: false, + }, { + handler: svc.handleGetSettingsAll, + pattern: routePatternGetV1SettingsAll, + isJSON: true, + }, { + handler: svc.handlePatchSettingsDNS, + pattern: routePatternPatchV1SettingsDNS, + isJSON: true, + }, { + handler: svc.handlePatchSettingsHTTP, + pattern: routePatternPatchV1SettingsHTTP, + isJSON: true, + }, { + handler: svc.handleGetV1SystemInfo, + pattern: routePatternGetV1SystemInfo, + isJSON: true, + }} + + logMw := httputil.NewLogMiddleware(svc.logger, slog.LevelDebug) + for _, r := range routes { + var hdlr http.Handler + if r.isJSON { + hdlr = jsonMw(r.handler) + } else { + hdlr = r.handler + } + + mux.Handle(r.pattern, logMw.Wrap(hdlr)) + } +} diff --git a/internal/next/websvc/websvc.go b/internal/next/websvc/websvc.go index dd4d8bdb..58e08390 100644 --- a/internal/next/websvc/websvc.go +++ b/internal/next/websvc/websvc.go @@ -83,7 +83,8 @@ func New(c *Config) (svc *Service, err error) { forceHTTPS: c.ForceHTTPS, } - mux := newMux(svc) + mux := http.NewServeMux() + svc.route(mux) if svc.overrideAddr != (netip.AddrPort{}) { svc.servers = []*server{newServer(svc.logger, svc.overrideAddr, nil, mux, c.Timeout)} @@ -160,56 +161,6 @@ func newServer( } } -// newMux returns a new HTTP request multiplexer for the AdGuard Home web -// service. -func newMux(svc *Service) (mux *http.ServeMux) { - mux = http.NewServeMux() - - routes := []struct { - handler http.HandlerFunc - pattern string - isJSON bool - }{{ - handler: svc.handleGetHealthCheck, - pattern: routePatternHealthCheck, - isJSON: false, - }, { - handler: http.FileServer(http.FS(svc.frontend)).ServeHTTP, - pattern: routePatternFrontend, - isJSON: false, - }, { - handler: svc.handleGetSettingsAll, - pattern: routePatternGetV1SettingsAll, - isJSON: true, - }, { - handler: svc.handlePatchSettingsDNS, - pattern: routePatternPatchV1SettingsDNS, - isJSON: true, - }, { - handler: svc.handlePatchSettingsHTTP, - pattern: routePatternPatchV1SettingsHTTP, - isJSON: true, - }, { - handler: svc.handleGetV1SystemInfo, - pattern: routePatternGetV1SystemInfo, - isJSON: true, - }} - - logMw := httputil.NewLogMiddleware(svc.logger, slog.LevelDebug) - for _, r := range routes { - var hdlr http.Handler - if r.isJSON { - hdlr = jsonMw(r.handler) - } else { - hdlr = r.handler - } - - mux.Handle(r.pattern, logMw.Wrap(hdlr)) - } - - return mux -} - // addrs returns all addresses on which this server serves the HTTP API. addrs // must not be called simultaneously with Start. If svc was initialized with // ":0" addresses, addrs will not return the actual bound ports until Start is @@ -249,7 +200,7 @@ var _ agh.ServiceWithConfig[*Config] = (*Service)(nil) // After Start exits, all HTTP servers have tried to start, possibly failing and // writing error messages to the log. // -// TODO(a.garipov): Use the context. +// TODO(a.garipov): Use the context for cancelation as well. func (svc *Service) Start(ctx context.Context) (err error) { if svc == nil { return nil