-
Notifications
You must be signed in to change notification settings - Fork 56
/
matcher.go
99 lines (79 loc) · 2.29 KB
/
matcher.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
package matcher
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/pkg/errors"
"github.com/savaki/jq"
)
func New(matcherName string) (MatcherFunc, bool) {
mf, found := matcherMux[matcherName]
return mf, found
}
type MatcherFunc func(request *http.Request, shardExpr string) (shardKey string, err error)
var matcherMux = map[string]MatcherFunc{
"header": func(req *http.Request, expr string) (string, error) {
return req.Header.Get(expr), nil
},
"multi-headers": func(req *http.Request, expr string) (string, error) {
headers := strings.Split(expr, ",")
var headerValues strings.Builder
headersCount := len(headers)
if headersCount == 0 {
return "", nil
}
for idx, header := range headers {
headerValue := req.Header.Get(header)
headerValues.Grow(len(headerValue))
headerValues.WriteString(headerValue)
if (idx + 1) != headersCount {
headerValues.Grow(1)
headerValues.WriteString(",")
}
}
return headerValues.String(), nil
},
"param": func(req *http.Request, expr string) (string, error) {
return req.URL.Query().Get(expr), nil
},
"path": func(req *http.Request, expr string) (string, error) {
rex := regexp.MustCompile(expr)
match := rex.FindStringSubmatch(req.URL.Path)
if len(match) == 0 {
return "", fmt.Errorf("no match found for expr: %s", expr)
}
return match[1], nil
},
"body": func(req *http.Request, expr string) (string, error) {
requestBody, err := ioutil.ReadAll(req.Body)
if err != nil {
return "", errors.Wrapf(err, "failed to read request body for expr: %s", expr)
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody))
var bodyKey interface{}
op, err := jq.Parse(expr)
if err != nil {
return "", errors.Wrapf(err, "failed to parse shard expr: %s", expr)
}
key, err := op.Apply(requestBody)
if err != nil {
return "", errors.Wrapf(err, "failed to apply parsed shard expr: %s", expr)
}
if err := json.Unmarshal(key, &bodyKey); err != nil {
return "", errors.Wrapf(err, "failed to unmarshal data for shard expr: %s", expr)
}
switch v := bodyKey.(type) {
case string:
return v, nil
case float64:
return strconv.FormatFloat(v, 'f', -1, 64), nil
default:
return "", errors.New("failed to type assert bodyKey")
}
},
}