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
}