-
Notifications
You must be signed in to change notification settings - Fork 349
/
interval.go
160 lines (131 loc) · 3.93 KB
/
interval.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
Package interval implements custom predicates to match routes
only during some period of time.
Package includes three predicates:
Between, Before and After. All predicates can be created using the date
represented as a string in RFC3339 format (see https://golang.org/pkg/time/#pkg-constants),
int64 or float64 number. float64 number will be converted into int64
number.
Between predicate matches only if current date is inside the specified
range of dates. Between predicate requires two dates to be constructed.
Upper boundary must be after lower boundary. Range includes the lower
boundary, but excludes the upper boundary.
Before predicate matches only if current date is before the specified
date. Only one date is required to construct the predicate.
After predicate matches only if current date is after or equal to
the specified date. Only one date is required to construct the predicate.
Examples:
example1: Path("/zalando") && Between("2016-01-01T12:00:00+02:00", "2016-02-01T12:00:00+02:00") -> "https://www.zalando.de";
example2: Path("/zalando") && Between(1451642400, 1454320800) -> "https://www.zalando.de";
example3: Path("/zalando") && Before("2016-02-01T12:00:00+02:00") -> "https://www.zalando.de";
example4: Path("/zalando") && Before(1454320800) -> "https://www.zalando.de";
example3: Path("/zalando") && After("2016-01-01T12:00:00+02:00") -> "https://www.zalando.de";
example4: Path("/zalando") && After(1451642400) -> "https://www.zalando.de";
*/
package interval
import (
"net/http"
"time"
"github.com/zalando/skipper/predicates"
"github.com/zalando/skipper/routing"
)
type intervalType int
const (
between intervalType = iota
before
after
)
type spec struct {
typ intervalType
}
type predicate struct {
typ intervalType
begin time.Time
end time.Time
getTime func() time.Time
}
// Creates Between predicate.
func NewBetween() routing.PredicateSpec { return &spec{between} }
// Creates Before predicate.
func NewBefore() routing.PredicateSpec { return &spec{before} }
// Creates After predicate.
func NewAfter() routing.PredicateSpec { return &spec{after} }
func (s *spec) Name() string {
switch s.typ {
case between:
return "Between"
case before:
return "Before"
case after:
return "After"
default:
panic("invalid interval predicate type")
}
}
func (s *spec) Create(args []interface{}) (routing.Predicate, error) {
switch s.typ {
case between:
if len(args) != 2 {
return nil, predicates.ErrInvalidPredicateParameters
}
default:
if len(args) != 1 {
return nil, predicates.ErrInvalidPredicateParameters
}
}
defaultGetTime := func() time.Time {
return time.Now()
}
switch s.typ {
case between:
if begin, end, ok := parseArgs(args[0], args[1]); ok {
if begin.Before(end) {
return &predicate{s.typ, begin, end, defaultGetTime}, nil
}
}
case before:
if end, ok := parseArg(args[0]); ok {
return &predicate{typ: s.typ, end: end, getTime: defaultGetTime}, nil
}
case after:
if begin, ok := parseArg(args[0]); ok {
return &predicate{typ: s.typ, begin: begin, getTime: defaultGetTime}, nil
}
}
return nil, predicates.ErrInvalidPredicateParameters
}
func parseArgs(arg1, arg2 interface{}) (time.Time, time.Time, bool) {
if begin, ok := parseArg(arg1); ok {
if end, ok := parseArg(arg2); ok {
return begin, end, true
}
}
return time.Time{}, time.Time{}, false
}
func parseArg(arg interface{}) (time.Time, bool) {
switch a := arg.(type) {
case string:
t, err := time.Parse(time.RFC3339, a)
if err == nil {
return t, true
}
case float64:
return time.Unix(int64(a), 0), true
case int64:
return time.Unix(a, 0), true
}
return time.Time{}, false
}
func (p *predicate) Match(r *http.Request) bool {
now := p.getTime()
switch p.typ {
case between:
return (p.begin.Before(now) || p.begin.Equal(now)) && p.end.After(now)
case before:
return p.end.After(now)
case after:
return p.begin.Before(now) || p.begin.Equal(now)
default:
return false
}
}