@marcel91200 / caddy Mirror · read-only / commits / ef6e53bb
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

core: Add support for `d` duration unit (#3323)

* caddy: Add support for `d` duration unit * Improvements to ParseDuration; add unit tests Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>

Francis Lavoie committed May 11, 2020 at 18:41 UTC ef6e53bb5f521e4d400849b79bc72e89fe2a7484
7 files changed +167 -18
caddy.go
+30 -2
@@ -486,7 +486,7 @@ func Validate(cfg *Config) error {
486 // Duration can be an integer or a string. An integer is
487 // interpreted as nanoseconds. If a string, it is a Go
488 // time.Duration value such as `300ms`, `1.5h`, or `2h45m`;
489 -// valid units are `ns`, `us`/`µs`, `ms`, `s`, `m`, and `h`.
489 +// valid units are `ns`, `us`/`µs`, `ms`, `s`, `m`, `h`, and `d`.
490 type Duration time.Duration
491
492 // UnmarshalJSON satisfies json.Unmarshaler.
@@ -497,7 +497,7 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
497 var dur time.Duration
498 var err error
499 if b[0] == byte('"') && b[len(b)-1] == byte('"') {
500 - dur, err = time.ParseDuration(strings.Trim(string(b), `"`))
500 + dur, err = ParseDuration(strings.Trim(string(b), `"`))
501 } else {
502 err = json.Unmarshal(b, &dur)
503 }
@@ -505,6 +505,34 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
505 return err
506 }
507
508 +// ParseDuration parses a duration string, adding
509 +// support for the "d" unit meaning number of days,
510 +// where a day is assumed to be 24h.
511 +func ParseDuration(s string) (time.Duration, error) {
512 + var inNumber bool
513 + var numStart int
514 + for i := 0; i < len(s); i++ {
515 + ch := s[i]
516 + if ch == 'd' {
517 + daysStr := s[numStart:i]
518 + days, err := strconv.ParseFloat(daysStr, 64)
519 + if err != nil {
520 + return 0, err
521 + }
522 + hours := days * 24.0
523 + hoursStr := strconv.FormatFloat(hours, 'f', -1, 64)
524 + s = s[:numStart] + hoursStr + "h" + s[i+1:]
525 + i--
526 + continue
527 + }
528 + if !inNumber {
529 + numStart = i
530 + }
531 + inNumber = (ch >= '0' && ch <= '9') || ch == '.' || ch == '-' || ch == '+'
532 + }
533 + return time.ParseDuration(s)
534 +}
535 +
536 // GoModule returns the build info of this Caddy
537 // build from debug.BuildInfo (requires Go modules).
538 // If no version information is available, a non-nil
caddy_test.go new
+74
@@ -0,0 +1,74 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +package caddy
16 +
17 +import (
18 + "testing"
19 + "time"
20 +)
21 +
22 +func TestParseDuration(t *testing.T) {
23 + const day = 24 * time.Hour
24 + for i, tc := range []struct {
25 + input string
26 + expect time.Duration
27 + }{
28 + {
29 + input: "3h",
30 + expect: 3 * time.Hour,
31 + },
32 + {
33 + input: "1d",
34 + expect: day,
35 + },
36 + {
37 + input: "1d30m",
38 + expect: day + 30*time.Minute,
39 + },
40 + {
41 + input: "1m2d",
42 + expect: time.Minute + day*2,
43 + },
44 + {
45 + input: "1m2d30s",
46 + expect: time.Minute + day*2 + 30*time.Second,
47 + },
48 + {
49 + input: "1d2d",
50 + expect: 3 * day,
51 + },
52 + {
53 + input: "1.5d",
54 + expect: time.Duration(1.5 * float64(day)),
55 + },
56 + {
57 + input: "4m1.25d",
58 + expect: 4*time.Minute + time.Duration(1.25*float64(day)),
59 + },
60 + {
61 + input: "-1.25d12h",
62 + expect: time.Duration(-1.25*float64(day)) - 12*time.Hour,
63 + },
64 + } {
65 + actual, err := ParseDuration(tc.input)
66 + if err != nil {
67 + t.Errorf("Test %d ('%s'): Got error: %v", i, tc.input, err)
68 + continue
69 + }
70 + if actual != tc.expect {
71 + t.Errorf("Test %d ('%s'): Expected=%s Actual=%s", i, tc.input, tc.expect, actual)
72 + }
73 + }
74 +}
caddyconfig/httpcaddyfile/options.go
+1 -2
@@ -16,7 +16,6 @@ package httpcaddyfile
16
17 import (
18 "strconv"
19 - "time"
19
20 "github.com/caddyserver/caddy/v2"
21 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -227,7 +226,7 @@ func parseOptOnDemand(d *caddyfile.Dispenser) (interface{}, error) {
226 if !d.NextArg() {
227 return nil, d.ArgErr()
228 }
230 - dur, err := time.ParseDuration(d.Val())
229 + dur, err := caddy.ParseDuration(d.Val())
230 if err != nil {
231 return nil, err
232 }
caddytest/integration/caddyfile_adapt_test.go
+50
@@ -489,3 +489,53 @@ func TestGlobalOptions(t *testing.T) {
489 }
490 }`)
491 }
492 +
493 +func TestLogRollDays(t *testing.T) {
494 + caddytest.AssertAdapt(t, `
495 + :80
496 +
497 + log {
498 + output file /var/log/access.log {
499 + roll_size 1gb
500 + roll_keep 5
501 + roll_keep_for 90d
502 + }
503 + }
504 + `, "caddyfile", `{
505 + "logging": {
506 + "logs": {
507 + "default": {
508 + "exclude": [
509 + "http.log.access.log0"
510 + ]
511 + },
512 + "log0": {
513 + "writer": {
514 + "filename": "/var/log/access.log",
515 + "output": "file",
516 + "roll_keep": 5,
517 + "roll_keep_days": 90,
518 + "roll_size_mb": 954
519 + },
520 + "include": [
521 + "http.log.access.log0"
522 + ]
523 + }
524 + }
525 + },
526 + "apps": {
527 + "http": {
528 + "servers": {
529 + "srv0": {
530 + "listen": [
531 + ":80"
532 + ],
533 + "logs": {
534 + "default_logger_name": "log0"
535 + }
536 + }
537 + }
538 + }
539 + }
540 +}`)
541 +}
cmd/main.go
+1 -1
@@ -311,7 +311,7 @@ func (f Flags) Float64(name string) float64 {
311 // is not a duration type. It panics if the flag is
312 // not in the flag set.
313 func (f Flags) Duration(name string) time.Duration {
314 - val, _ := time.ParseDuration(f.String(name))
314 + val, _ := caddy.ParseDuration(f.String(name))
315 return val
316 }
317
modules/caddyhttp/reverseproxy/caddyfile.go
+10 -11
@@ -21,7 +21,6 @@ import (
21 "reflect"
22 "strconv"
23 "strings"
24 - "time"
24
25 "github.com/caddyserver/caddy/v2"
26 "github.com/caddyserver/caddy/v2/caddyconfig"
@@ -250,7 +249,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
249 if h.LoadBalancing == nil {
250 h.LoadBalancing = new(LoadBalancing)
251 }
253 - dur, err := time.ParseDuration(d.Val())
252 + dur, err := caddy.ParseDuration(d.Val())
253 if err != nil {
254 return d.Errf("bad duration value %s: %v", d.Val(), err)
255 }
@@ -263,7 +262,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
262 if h.LoadBalancing == nil {
263 h.LoadBalancing = new(LoadBalancing)
264 }
266 - dur, err := time.ParseDuration(d.Val())
265 + dur, err := caddy.ParseDuration(d.Val())
266 if err != nil {
267 return d.Errf("bad interval value '%s': %v", d.Val(), err)
268 }
@@ -307,7 +306,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
306 if h.HealthChecks.Active == nil {
307 h.HealthChecks.Active = new(ActiveHealthChecks)
308 }
310 - dur, err := time.ParseDuration(d.Val())
309 + dur, err := caddy.ParseDuration(d.Val())
310 if err != nil {
311 return d.Errf("bad interval value %s: %v", d.Val(), err)
312 }
@@ -323,7 +322,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
322 if h.HealthChecks.Active == nil {
323 h.HealthChecks.Active = new(ActiveHealthChecks)
324 }
326 - dur, err := time.ParseDuration(d.Val())
325 + dur, err := caddy.ParseDuration(d.Val())
326 if err != nil {
327 return d.Errf("bad timeout value %s: %v", d.Val(), err)
328 }
@@ -387,7 +386,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
386 if h.HealthChecks.Passive == nil {
387 h.HealthChecks.Passive = new(PassiveHealthChecks)
388 }
390 - dur, err := time.ParseDuration(d.Val())
389 + dur, err := caddy.ParseDuration(d.Val())
390 if err != nil {
391 return d.Errf("bad duration value '%s': %v", d.Val(), err)
392 }
@@ -441,7 +440,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
440 if h.HealthChecks.Passive == nil {
441 h.HealthChecks.Passive = new(PassiveHealthChecks)
442 }
444 - dur, err := time.ParseDuration(d.Val())
443 + dur, err := caddy.ParseDuration(d.Val())
444 if err != nil {
445 return d.Errf("bad duration value '%s': %v", d.Val(), err)
446 }
@@ -454,7 +453,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
453 if fi, err := strconv.Atoi(d.Val()); err == nil {
454 h.FlushInterval = caddy.Duration(fi)
455 } else {
457 - dur, err := time.ParseDuration(d.Val())
456 + dur, err := caddy.ParseDuration(d.Val())
457 if err != nil {
458 return d.Errf("bad duration value '%s': %v", d.Val(), err)
459 }
@@ -606,7 +605,7 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
605 if !d.NextArg() {
606 return d.ArgErr()
607 }
609 - dur, err := time.ParseDuration(d.Val())
608 + dur, err := caddy.ParseDuration(d.Val())
609 if err != nil {
610 return d.Errf("bad timeout value '%s': %v", d.Val(), err)
611 }
@@ -641,7 +640,7 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
640 if !d.NextArg() {
641 return d.ArgErr()
642 }
644 - dur, err := time.ParseDuration(d.Val())
643 + dur, err := caddy.ParseDuration(d.Val())
644 if err != nil {
645 return d.Errf("bad timeout value '%s': %v", d.Val(), err)
646 }
@@ -683,7 +682,7 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
682 h.KeepAlive.Enabled = &disable
683 break
684 }
686 - dur, err := time.ParseDuration(d.Val())
685 + dur, err := caddy.ParseDuration(d.Val())
686 if err != nil {
687 return d.Errf("bad duration value '%s': %v", d.Val(), err)
688 }
modules/logging/filewriter.go
+1 -2
@@ -21,7 +21,6 @@ import (
21 "os"
22 "path/filepath"
23 "strconv"
24 - "time"
24
25 "github.com/caddyserver/caddy/v2"
26 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -194,7 +193,7 @@ func (fw *FileWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
193 if !d.AllArgs(&keepForStr) {
194 return d.ArgErr()
195 }
197 - keepFor, err := time.ParseDuration(keepForStr)
196 + keepFor, err := caddy.ParseDuration(keepForStr)
197 if err != nil {
198 return d.Errf("parsing roll_keep_for duration: %v", err)
199 }