diff --git a/.goreleaser.yml b/.goreleaser.yml index 005fdbaf3..c7ed4b365 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -111,7 +111,7 @@ archives: - id: default format_overrides: - goos: windows - format: zip + formats: zip name_template: >- {{ .ProjectName }}_ {{- .Version }}_ diff --git a/caddytest/integration/caddyfile_adapt/header.caddyfiletest b/caddytest/integration/caddyfile_adapt/header.caddyfiletest index ec2a842a3..a0af24ff6 100644 --- a/caddytest/integration/caddyfile_adapt/header.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/header.caddyfiletest @@ -12,10 +12,14 @@ @images path /images/* header @images { Cache-Control "public, max-age=3600, stale-while-revalidate=86400" + match { + status 200 + } } header { +Link "Foo" +Link "Bar" + match status 200 } header >Set Defer header >Replace Deferred Replacement @@ -42,6 +46,11 @@ { "handler": "headers", "response": { + "require": { + "status_code": [ + 200 + ] + }, "set": { "Cache-Control": [ "public, max-age=3600, stale-while-revalidate=86400" @@ -136,6 +145,11 @@ "Foo", "Bar" ] + }, + "require": { + "status_code": [ + 200 + ] } } }, diff --git a/go.mod b/go.mod index 67443562f..35f06bcd8 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/Masterminds/sprig/v3 v3.3.0 github.com/alecthomas/chroma/v2 v2.14.0 github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b - github.com/caddyserver/certmagic v0.21.5 + github.com/caddyserver/certmagic v0.21.7 github.com/caddyserver/zerossl v0.1.3 github.com/dustin/go-humanize v1.0.1 github.com/go-chi/chi/v5 v5.0.12 @@ -17,9 +17,9 @@ require ( github.com/google/uuid v1.6.0 github.com/klauspost/compress v1.17.11 github.com/klauspost/cpuid/v2 v2.2.9 - github.com/mholt/acmez/v3 v3.0.0 + github.com/mholt/acmez/v3 v3.0.1 github.com/prometheus/client_golang v1.19.1 - github.com/quic-go/quic-go v0.48.2 + github.com/quic-go/quic-go v0.49.0 github.com/smallstep/certificates v0.26.1 github.com/smallstep/nosql v0.6.1 github.com/smallstep/truststore v0.13.0 @@ -74,7 +74,7 @@ require ( go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 // indirect go.opentelemetry.io/contrib/propagators/ot v1.17.0 // indirect - go.uber.org/mock v0.4.0 // indirect + go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect diff --git a/go.sum b/go.sum index 538304a28..b5622ba51 100644 --- a/go.sum +++ b/go.sum @@ -89,8 +89,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/caddyserver/certmagic v0.21.5 h1:iIga4nZRgd27EIEbX7RZmoRMul+EVBn/h7bAGL83dnY= -github.com/caddyserver/certmagic v0.21.5/go.mod h1:n1sCo7zV1Ez2j+89wrzDxo4N/T1Ws/Vx8u5NvuBFabw= +github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= +github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -344,8 +344,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E= -github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= +github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8= +github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= @@ -392,8 +392,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= -github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= +github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94= +github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= @@ -564,8 +564,8 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= diff --git a/modules/caddyhttp/app.go b/modules/caddyhttp/app.go index 850d3aa8f..5477ed8fe 100644 --- a/modules/caddyhttp/app.go +++ b/modules/caddyhttp/app.go @@ -529,21 +529,6 @@ func (app *App) Start() error { // enable TLS if there is a policy and if this is not the HTTP port useTLS := len(srv.TLSConnPolicies) > 0 && int(listenAddr.StartPort+portOffset) != app.httpPort() - // enable HTTP/3 if configured - if h3ok && useTLS { - app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport)) - if err := srv.serveHTTP3(listenAddr.At(portOffset), tlsCfg); err != nil { - return err - } - } - - if h3ok && !useTLS { - // Can only serve h3 with TLS enabled - app.logger.Warn("HTTP/3 skipped because it requires TLS", - zap.String("network", listenAddr.Network), - zap.String("addr", hostport)) - } - if h1ok || h2ok && useTLS || h2cok { // create the listener for this socket lnAny, err := listenAddr.Listen(app.ctx, portOffset, net.ListenConfig{KeepAlive: time.Duration(srv.KeepAliveInterval)}) @@ -614,6 +599,33 @@ func (app *App) Start() error { zap.String("network", listenAddr.Network), zap.String("addr", hostport)) } + + if h3ok { + // Can't serve HTTP/3 on the same socket as HTTP/1 and 2 because it uses + // a different transport mechanism... which is fine, but the OS doesn't + // differentiate between a SOCK_STREAM file and a SOCK_DGRAM file; they + // are still one file on the system. So even though "unixpacket" and + // "unixgram" are different network types just as "tcp" and "udp" are, + // the OS will not let us use the same file as both STREAM and DGRAM. + if listenAddr.IsUnixNetwork() { + app.logger.Warn("HTTP/3 disabled because Unix can't multiplex STREAM and DGRAM on same socket", + zap.String("file", hostport)) + continue + } + + if useTLS { + // enable HTTP/3 if configured + app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport)) + if err := srv.serveHTTP3(listenAddr.At(portOffset), tlsCfg); err != nil { + return err + } + } else { + // Can only serve h3 with TLS enabled + app.logger.Warn("HTTP/3 skipped because it requires TLS", + zap.String("network", listenAddr.Network), + zap.String("addr", hostport)) + } + } } } diff --git a/modules/caddyhttp/headers/caddyfile.go b/modules/caddyhttp/headers/caddyfile.go index e55e9fab2..f060471b1 100644 --- a/modules/caddyhttp/headers/caddyfile.go +++ b/modules/caddyhttp/headers/caddyfile.go @@ -99,6 +99,16 @@ func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) handler.Response.Deferred = true continue } + if field == "match" { + responseMatchers := make(map[string]caddyhttp.ResponseMatcher) + err := caddyhttp.ParseNamedResponseMatcher(h.NewFromNextSegment(), responseMatchers) + if err != nil { + return nil, err + } + matcher := responseMatchers["match"] + handler.Response.Require = &matcher + continue + } if hasArgs { return nil, h.Err("cannot specify headers in both arguments and block") // because it would be weird } diff --git a/modules/caddyhttp/headers/headers_test.go b/modules/caddyhttp/headers/headers_test.go index d74e6fc3a..9808c29c9 100644 --- a/modules/caddyhttp/headers/headers_test.go +++ b/modules/caddyhttp/headers/headers_test.go @@ -143,6 +143,28 @@ func TestHandler(t *testing.T) { "Cache-Control": []string{"no-cache"}, }, }, + { // same as above, but checks that response headers are left alone when "Require" conditions are unmet + handler: Handler{ + Response: &RespHeaderOps{ + Require: &caddyhttp.ResponseMatcher{ + Headers: http.Header{ + "Cache-Control": nil, + }, + }, + HeaderOps: &HeaderOps{ + Add: http.Header{ + "Cache-Control": []string{"no-cache"}, + }, + }, + }, + }, + respHeader: http.Header{ + "Cache-Control": []string{"something"}, + }, + expectedRespHeader: http.Header{ + "Cache-Control": []string{"something"}, + }, + }, { handler: Handler{ Response: &RespHeaderOps{ diff --git a/modules/caddyhttp/logging.go b/modules/caddyhttp/logging.go index 0a389fe16..87298ac3c 100644 --- a/modules/caddyhttp/logging.go +++ b/modules/caddyhttp/logging.go @@ -211,6 +211,11 @@ func errLogValues(err error) (status int, msg string, fields func() []zapcore.Fi } return } + fields = func() []zapcore.Field { + return []zapcore.Field{ + zap.Error(err), + } + } status = http.StatusInternalServerError msg = err.Error() return diff --git a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go index 6fe7df3fd..5db73a4a2 100644 --- a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go @@ -131,15 +131,18 @@ func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { // is equivalent to a route consisting of: // // # Add trailing slash for directory requests +// # This redirection is automatically disabled if "{http.request.uri.path}/index.php" +// # doesn't appear in the try_files list // @canonicalPath { // file {path}/index.php // not path */ // } // redir @canonicalPath {path}/ 308 // -// # If the requested file does not exist, try index files +// # If the requested file does not exist, try index files and assume index.php always exists // @indexFiles file { // try_files {path} {path}/index.php index.php +// try_policy first_exist_fallback // split_path .php // } // rewrite @indexFiles {http.matchers.file.relative} diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index 230bec951..a15dbe424 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -683,7 +683,7 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http. req.Header.Set("Early-Data", "1") } - reqUpType := upgradeType(req.Header) + reqUpgradeType := upgradeType(req.Header) removeConnectionHeaders(req.Header) // Remove hop-by-hop headers to the backend. Especially @@ -704,9 +704,9 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http. // After stripping all the hop-by-hop connection headers above, add back any // necessary for protocol upgrades, such as for websockets. - if reqUpType != "" { + if reqUpgradeType != "" { req.Header.Set("Connection", "Upgrade") - req.Header.Set("Upgrade", reqUpType) + req.Header.Set("Upgrade", reqUpgradeType) normalizeWebsocketHeaders(req.Header) } @@ -732,6 +732,9 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http. return nil, err } + // Via header(s) + req.Header.Add("Via", fmt.Sprintf("%d.%d Caddy", req.ProtoMajor, req.ProtoMinor)) + return req, nil } @@ -882,13 +885,15 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origRe }), ) + const logMessage = "upstream roundtrip" + if err != nil { - if c := logger.Check(zapcore.DebugLevel, "upstream roundtrip"); c != nil { + if c := logger.Check(zapcore.DebugLevel, logMessage); c != nil { c.Write(zap.Error(err)) } return err } - if c := logger.Check(zapcore.DebugLevel, "upstream roundtrip"); c != nil { + if c := logger.Check(zapcore.DebugLevel, logMessage); c != nil { c.Write( zap.Object("headers", caddyhttp.LoggableHTTPHeader{ Header: res.Header, @@ -1024,6 +1029,14 @@ func (h *Handler) finalizeResponse( res.Header.Del(h) } + // delete our Server header and use Via instead (see #6275) + rw.Header().Del("Server") + var protoPrefix string + if !strings.HasPrefix(strings.ToUpper(res.Proto), "HTTP/") { + protoPrefix = res.Proto[:strings.Index(res.Proto, "/")+1] + } + rw.Header().Add("Via", fmt.Sprintf("%s%d.%d Caddy", protoPrefix, res.ProtoMajor, res.ProtoMinor)) + // apply any response header operations if h.Headers != nil && h.Headers.Response != nil { if h.Headers.Response.Require == nil || diff --git a/modules/caddytls/acmeissuer.go b/modules/caddytls/acmeissuer.go index 29a5954e7..2fe5eec97 100644 --- a/modules/caddytls/acmeissuer.go +++ b/modules/caddytls/acmeissuer.go @@ -60,6 +60,14 @@ type ACMEIssuer struct { // other than ACME transactions. Email string `json:"email,omitempty"` + // Optionally select an ACME profile to use for certificate + // orders. Must be a profile name offered by the ACME server, + // which are listed at its directory endpoint. + // + // EXPERIMENTAL: Subject to change. + // See https://datatracker.ietf.org/doc/draft-aaron-acme-profiles/ + Profile string `json:"profile,omitempty"` + // If you have an existing account with the ACME server, put // the private key here in PEM format. The ACME client will // look up your account information with this key first before @@ -184,6 +192,7 @@ func (iss *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEIssuer, error) { CA: iss.CA, TestCA: iss.TestCA, Email: iss.Email, + Profile: iss.Profile, AccountKeyPEM: iss.AccountKey, CertObtainTimeout: time.Duration(iss.ACMETimeout), TrustedRoots: iss.rootPool, @@ -338,6 +347,7 @@ func (iss *ACMEIssuer) generateZeroSSLEABCredentials(ctx context.Context, acct a // dir // test_dir // email +// profile // timeout // disable_http_challenge // disable_tlsalpn_challenge @@ -400,6 +410,11 @@ func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return d.ArgErr() } + case "profile": + if !d.AllArgs(&iss.Profile) { + return d.ArgErr() + } + case "timeout": var timeoutStr string if !d.AllArgs(&timeoutStr) { diff --git a/modules/logging/filewriter.go b/modules/logging/filewriter.go index 62d500dca..ef3211cbb 100644 --- a/modules/logging/filewriter.go +++ b/modules/logging/filewriter.go @@ -20,6 +20,7 @@ import ( "io" "math" "os" + "path/filepath" "strconv" "github.com/dustin/go-humanize" @@ -146,51 +147,68 @@ func (fw FileWriter) WriterKey() string { // OpenWriter opens a new file writer. func (fw FileWriter) OpenWriter() (io.WriteCloser, error) { - if fw.Mode == 0 { - fw.Mode = 0o600 + modeIfCreating := os.FileMode(fw.Mode) + if modeIfCreating == 0 { + modeIfCreating = 0o600 } - // roll log files by default - if fw.Roll == nil || *fw.Roll { - if fw.RollSizeMB == 0 { - fw.RollSizeMB = 100 - } - if fw.RollCompress == nil { - compress := true - fw.RollCompress = &compress - } - if fw.RollKeep == 0 { - fw.RollKeep = 10 - } - if fw.RollKeepDays == 0 { - fw.RollKeepDays = 90 - } + // roll log files as a sensible default to avoid disk space exhaustion + roll := fw.Roll == nil || *fw.Roll - // create the file if it does not exist with the right mode. - // lumberjack will reuse the file mode across log rotation. - f_tmp, err := os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(fw.Mode)) + // create the file if it does not exist; create with the configured mode, or default + // to restrictive if not set. (lumberjack will reuse the file mode across log rotation) + if err := os.MkdirAll(filepath.Dir(fw.Filename), 0o700); err != nil { + return nil, err + } + file, err := os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, modeIfCreating) + if err != nil { + return nil, err + } + info, err := file.Stat() + if roll { + file.Close() // lumberjack will reopen it on its own + } + + // Ensure already existing files have the right mode, since OpenFile will not set the mode in such case. + if configuredMode := os.FileMode(fw.Mode); configuredMode != 0 { if err != nil { - return nil, err + return nil, fmt.Errorf("unable to stat log file to see if we need to set permissions: %v", err) } - f_tmp.Close() - // ensure already existing files have the right mode, - // since OpenFile will not set the mode in such case. - if err = os.Chmod(fw.Filename, os.FileMode(fw.Mode)); err != nil { - return nil, err + // only chmod if the configured mode is different + if info.Mode()&os.ModePerm != configuredMode&os.ModePerm { + if err = os.Chmod(fw.Filename, configuredMode); err != nil { + return nil, err + } } - - return &lumberjack.Logger{ - Filename: fw.Filename, - MaxSize: fw.RollSizeMB, - MaxAge: fw.RollKeepDays, - MaxBackups: fw.RollKeep, - LocalTime: fw.RollLocalTime, - Compress: *fw.RollCompress, - }, nil } - // otherwise just open a regular file - return os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(fw.Mode)) + // if not rolling, then the plain file handle is all we need + if !roll { + return file, nil + } + + // otherwise, return a rolling log + if fw.RollSizeMB == 0 { + fw.RollSizeMB = 100 + } + if fw.RollCompress == nil { + compress := true + fw.RollCompress = &compress + } + if fw.RollKeep == 0 { + fw.RollKeep = 10 + } + if fw.RollKeepDays == 0 { + fw.RollKeepDays = 90 + } + return &lumberjack.Logger{ + Filename: fw.Filename, + MaxSize: fw.RollSizeMB, + MaxAge: fw.RollKeepDays, + MaxBackups: fw.RollKeep, + LocalTime: fw.RollLocalTime, + Compress: *fw.RollCompress, + }, nil } // UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax: diff --git a/modules/logging/filewriter_test.go b/modules/logging/filewriter_test.go index 0c54a6590..f9072f98a 100644 --- a/modules/logging/filewriter_test.go +++ b/modules/logging/filewriter_test.go @@ -20,6 +20,7 @@ import ( "encoding/json" "os" "path" + "path/filepath" "syscall" "testing" @@ -77,7 +78,7 @@ func TestFileCreationMode(t *testing.T) { t.Fatalf("failed to create tempdir: %v", err) } defer os.RemoveAll(dir) - fpath := path.Join(dir, "test.log") + fpath := filepath.Join(dir, "test.log") tt.fw.Filename = fpath logger, err := tt.fw.OpenWriter() @@ -92,7 +93,7 @@ func TestFileCreationMode(t *testing.T) { } if st.Mode() != tt.wantMode { - t.Errorf("file mode is %v, want %v", st.Mode(), tt.wantMode) + t.Errorf("%s: file mode is %v, want %v", tt.name, st.Mode(), tt.wantMode) } }) }