-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfiltering.go
92 lines (75 loc) · 2.37 KB
/
filtering.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
package main
import (
"fmt"
"net/http"
"regexp"
"strings"
"github.com/bmatcuk/doublestar/v4"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
)
var apiPrefixRe = regexp.MustCompile(`^/v[0-9.]+`)
// RequestAccepter models a request accepter.
type RequestAccepter interface {
AcceptRequest(*http.Request) bool
}
// AllowFilter represents a request-allowing filter.
type AllowFilter struct {
Method string `toml:"method"` // HTTP method (exact match).
Path string `toml:"path"` // Request path (pattern match).
}
// Validate does filter validation.
func (a *AllowFilter) Validate() error {
if _, err := doublestar.Match(a.Path, ""); err != nil {
return fmt.Errorf("error validating path pattern `%s`: %w", a.Path, err)
}
return nil
}
// AcceptRequest implements RequestAccepter.
func (a *AllowFilter) AcceptRequest(r *http.Request) bool {
if r.Method != a.Method {
return false
}
pr := apiPrefixRe.FindString(r.URL.Path)
p := strings.TrimPrefix(r.URL.Path, pr)
// Ignore the error here, a pattern check is required before using this method.
if ok, _ := doublestar.Match(a.Path, p); !ok {
return false
}
return true
}
// RequestAccepters represents a slice of request accepters.
type RequestAccepters []RequestAccepter
// AcceptRequest implements RequestAccepter.
func (ras RequestAccepters) AcceptRequest(r *http.Request) (accepted bool) {
for _, ra := range ras {
if ra.AcceptRequest(r) {
accepted = true
break
}
}
return
}
// FilteringMiddleware wraps a handler and filters requests against rules.
type FilteringMiddleware struct {
wrapped http.Handler
accepter RequestAccepter
logger log.Logger
}
// NewFilteringMiddleware creates a filtering middleware.
func NewFilteringMiddleware(h http.Handler, a RequestAccepter, l log.Logger) *FilteringMiddleware {
return &FilteringMiddleware{h, a, l}
}
func logRequest(l log.Logger, r *http.Request, status string) {
l.Log("request_status", status, "method", r.Method, "path", r.URL.Path, "from", r.RemoteAddr)
}
// ServeHTTP implements http.Handler.
func (f *FilteringMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
logRequest(level.Debug(f.logger), r, "received")
if r.Method == http.MethodHead || f.accepter.AcceptRequest(r) {
f.wrapped.ServeHTTP(w, r)
} else {
logRequest(level.Warn(f.logger), r, "rejected")
http.Error(w, progName+": request rejected", http.StatusForbidden)
}
}