Skip to content

Commit 301d39d

Browse files
authored
feat: add handler interface that handlers must implement (#74)
* feat: add handler interface that handlers must implement * chore: lint
1 parent d5c8d69 commit 301d39d

14 files changed

+229
-144
lines changed

.golangci.yml

+8-16
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,10 @@ run:
44
linters:
55
enable-all: true
66
disable:
7-
- exhaustivestruct # deprecated
8-
- interfacer # deprecated
9-
- varcheck # deprecated
10-
- nosnakecase # deprecated
11-
- ifshort # deprecated
12-
- maligned # deprecated
13-
- deadcode # deprecated
14-
- scopelint # deprecated
15-
- structcheck # deprecated
16-
- golint # deprecated
17-
- rowserrcheck # deprecated
18-
- sqlclosecheck # deprecated
19-
- wastedassign # deprecated
7+
- gomnd # deprecated
8+
- execinquery # deprecated
209
- varnamelen # useless
21-
- paralleltest # false positive
22-
- tparallel # false positive
23-
- tagliatelle # Breaking: 'Rule' tag -> 'rule'
10+
- ireturn # Not relevant
2411

2512
issues:
2613
exclude-rules:
@@ -39,3 +26,8 @@ linters-settings:
3926
deny:
4027
- pkg: "github.com/instana/testify"
4128
desc: not allowed
29+
tagliatelle:
30+
case:
31+
use-field-name: true
32+
rules:
33+
yaml: goPascal

htransformation.go

+23-43
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66
"net/http"
7-
"regexp"
87

98
"github.com/tomMoulard/htransformation/pkg/handler/deleter"
109
"github.com/tomMoulard/htransformation/pkg/handler/join"
@@ -16,10 +15,9 @@ import (
1615

1716
// HeadersTransformation holds the necessary components of a Traefik plugin.
1817
type HeadersTransformation struct {
19-
name string
20-
next http.Handler
21-
rules []types.Rule
22-
ruleHandlers map[types.RuleType]func(http.ResponseWriter, *http.Request, types.Rule)
18+
name string
19+
next http.Handler
20+
handlers []types.Handler
2321
}
2422

2523
// Config holds configuration to be passed to the plugin.
@@ -36,64 +34,46 @@ func CreateConfig() *Config {
3634

3735
// New instantiates and returns the required components used to handle an HTTP request.
3836
func New(_ context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
39-
ruleHandlers := map[types.RuleType]func(http.ResponseWriter, *http.Request, types.Rule){
40-
types.Delete: deleter.Handle,
41-
types.Join: join.Handle,
42-
types.Rename: rename.Handle,
43-
types.RewriteValueRule: rewrite.Handle,
44-
types.Set: set.Handle,
37+
handlerBuilder := map[types.RuleType]func(types.Rule) (types.Handler, error){
38+
types.Delete: deleter.New,
39+
types.Join: join.New,
40+
types.Rename: rename.New,
41+
types.RewriteValueRule: rewrite.New,
42+
types.Set: set.New,
4543
}
4644

47-
validateRules := map[types.RuleType]func(types.Rule) error{
48-
types.Delete: deleter.Validate,
49-
types.Join: join.Validate,
50-
types.Rename: rename.Validate,
51-
types.RewriteValueRule: rewrite.Validate,
52-
types.Set: set.Validate,
53-
}
45+
handlers := make([]types.Handler, 0, len(config.Rules))
5446

55-
for i, rule := range config.Rules {
56-
if _, ok := ruleHandlers[rule.Type]; !ok {
47+
for _, rule := range config.Rules {
48+
newHandler, ok := handlerBuilder[rule.Type]
49+
if !ok {
5750
return nil, fmt.Errorf("%w: %s", types.ErrInvalidRuleType, rule.Name)
5851
}
5952

60-
validate, ok := validateRules[rule.Type]
61-
if !ok {
62-
continue
53+
h, err := newHandler(rule)
54+
if err != nil {
55+
return nil, fmt.Errorf("%w: %s", err, rule.Name)
6356
}
6457

65-
if err := validate(rule); err != nil {
58+
if err := h.Validate(); err != nil {
6659
return nil, fmt.Errorf("%w: %s", err, rule.Name)
6760
}
6861

69-
if rule.Type == types.Rename || rule.Type == types.RewriteValueRule {
70-
re, err := regexp.Compile(rule.Header)
71-
if err != nil { // must be validated before
72-
return nil, fmt.Errorf("%w: %s", types.ErrInvalidRegexp, rule.Name)
73-
}
74-
75-
config.Rules[i].Regexp = re
76-
}
62+
handlers = append(handlers, h)
7763
}
7864

7965
return &HeadersTransformation{
80-
name: name,
81-
next: next,
82-
rules: config.Rules,
83-
ruleHandlers: ruleHandlers,
66+
name: name,
67+
next: next,
68+
handlers: handlers,
8469
}, nil
8570
}
8671

8772
// Iterate over every header to match the ones specified in the config and
8873
// return nothing if regexp failed.
8974
func (u *HeadersTransformation) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
90-
for _, rule := range u.rules {
91-
ruleHandler, ok := u.ruleHandlers[rule.Type]
92-
if !ok {
93-
continue
94-
}
95-
96-
ruleHandler(responseWriter, request, rule)
75+
for _, handler := range u.handlers {
76+
handler.Handle(responseWriter, request)
9777
}
9878

9979
u.next.ServeHTTP(responseWriter, request)

htransformation_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
)
1414

1515
func TestValidation(t *testing.T) {
16+
t.Parallel()
17+
1618
testCases := []struct {
1719
name string
1820
config *plug.Config
@@ -108,6 +110,8 @@ func TestValidation(t *testing.T) {
108110
}
109111

110112
func TestHeaderRules(t *testing.T) {
113+
t.Parallel()
114+
111115
testCases := []struct {
112116
name string
113117
rule types.Rule

pkg/handler/deleter/delete.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,24 @@ import (
77
"github.com/tomMoulard/htransformation/pkg/utils/header"
88
)
99

10-
func Validate(types.Rule) error {
10+
type Deleter struct {
11+
rule *types.Rule
12+
}
13+
14+
func New(rule types.Rule) (types.Handler, error) {
15+
return &Deleter{rule: &rule}, nil
16+
}
17+
18+
func (d *Deleter) Validate() error {
1119
return nil
1220
}
1321

14-
func Handle(rw http.ResponseWriter, req *http.Request, rule types.Rule) {
15-
if rule.SetOnResponse {
16-
rw.Header().Del(rule.Header)
22+
func (d *Deleter) Handle(rw http.ResponseWriter, req *http.Request) {
23+
if d.rule.SetOnResponse {
24+
rw.Header().Del(d.rule.Header)
1725

1826
return
1927
}
2028

21-
header.Delete(req, rule.Header)
29+
header.Delete(req, d.rule.Header)
2230
}

pkg/handler/deleter/delete_test.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ func TestDeleteHandler(t *testing.T) {
7070
req.Header.Add(hName, hVal)
7171
}
7272

73-
deleter.Handle(nil, req, test.rule)
73+
deleteHandler, err := deleter.New(test.rule)
74+
require.NoError(t, err)
75+
76+
deleteHandler.Handle(nil, req)
7477

7578
for hName, hVal := range test.expectedHeaders {
7679
assert.Equal(t, hVal, req.Header.Get(hName))
@@ -130,7 +133,10 @@ func TestDeleteHandlerOnResponse(t *testing.T) {
130133
rw.Header().Add(hName, hVal)
131134
}
132135

133-
deleter.Handle(rw, nil, test.rule)
136+
deleteHandler, err := deleter.New(test.rule)
137+
require.NoError(t, err)
138+
139+
deleteHandler.Handle(rw, nil)
134140

135141
for hName, hVal := range test.want {
136142
assert.Equal(t, hVal, rw.Header().Get(hName))
@@ -140,6 +146,8 @@ func TestDeleteHandlerOnResponse(t *testing.T) {
140146
}
141147

142148
func TestValidation(t *testing.T) {
149+
t.Parallel()
150+
143151
testCases := []struct {
144152
name string
145153
rule types.Rule
@@ -163,7 +171,10 @@ func TestValidation(t *testing.T) {
163171
t.Run(test.name, func(t *testing.T) {
164172
t.Parallel()
165173

166-
err := deleter.Validate(test.rule)
174+
deleteHandler, err := deleter.New(test.rule)
175+
require.NoError(t, err)
176+
177+
err = deleteHandler.Validate()
167178
t.Log(err)
168179

169180
if test.wantErr {

pkg/handler/join/join.go

+19-11
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,50 @@ import (
77
"github.com/tomMoulard/htransformation/pkg/types"
88
)
99

10-
func Validate(rule types.Rule) error {
11-
if len(rule.Values) == 0 || rule.Sep == "" {
10+
type Join struct {
11+
rule *types.Rule
12+
}
13+
14+
func New(rule types.Rule) (types.Handler, error) {
15+
return &Join{rule: &rule}, nil
16+
}
17+
18+
func (j *Join) Validate() error {
19+
if len(j.rule.Values) == 0 || j.rule.Sep == "" {
1220
return types.ErrMissingRequiredFields
1321
}
1422

1523
return nil
1624
}
1725

18-
func Handle(rw http.ResponseWriter, req *http.Request, rule types.Rule) {
26+
func (j *Join) Handle(rw http.ResponseWriter, req *http.Request) {
1927
var val []string
20-
if strings.EqualFold(rule.Header, "Host") {
28+
if strings.EqualFold(j.rule.Header, "Host") {
2129
val = []string{req.Host}
2230
} else {
2331
var ok bool
24-
val, ok = req.Header[rule.Header]
32+
val, ok = req.Header[j.rule.Header]
2533

2634
if !ok {
2735
return
2836
}
2937
}
3038

3139
newHeaderVal := val[0]
32-
for _, value := range rule.Values {
33-
newHeaderVal += rule.Sep + getValue(value, rule.HeaderPrefix, req)
40+
for _, value := range j.rule.Values {
41+
newHeaderVal += j.rule.Sep + getValue(value, j.rule.HeaderPrefix, req)
3442
}
3543

36-
if rule.SetOnResponse {
37-
rw.Header().Set(rule.Name, newHeaderVal)
44+
if j.rule.SetOnResponse {
45+
rw.Header().Set(j.rule.Name, newHeaderVal)
3846

3947
return
4048
}
4149

42-
if strings.EqualFold(rule.Header, "Host") {
50+
if strings.EqualFold(j.rule.Header, "Host") {
4351
req.Host = newHeaderVal
4452
} else {
45-
req.Header.Set(rule.Header, newHeaderVal)
53+
req.Header.Set(j.rule.Header, newHeaderVal)
4654
}
4755
}
4856

pkg/handler/join/join_test.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
)
1313

1414
func TestJoinHandler(t *testing.T) {
15+
t.Parallel()
16+
1517
testCases := []struct {
1618
name string
1719
rule types.Rule
@@ -199,7 +201,10 @@ func TestJoinHandler(t *testing.T) {
199201
req.Header.Add(hName, hVal)
200202
}
201203

202-
join.Handle(nil, req, test.rule)
204+
joinHandler, err := join.New(test.rule)
205+
require.NoError(t, err)
206+
207+
joinHandler.Handle(nil, req)
203208

204209
for hName, hVal := range test.expectedHeaders {
205210
assert.Equal(t, hVal, req.Header.Get(hName))
@@ -212,6 +217,8 @@ func TestJoinHandler(t *testing.T) {
212217
}
213218

214219
func TestValidation(t *testing.T) {
220+
t.Parallel()
221+
215222
testCases := []struct {
216223
name string
217224
rule types.Rule
@@ -262,7 +269,10 @@ func TestValidation(t *testing.T) {
262269
t.Run(test.name, func(t *testing.T) {
263270
t.Parallel()
264271

265-
err := join.Validate(test.rule)
272+
joinHandler, err := join.New(test.rule)
273+
require.NoError(t, err)
274+
275+
err = joinHandler.Validate()
266276
t.Log(err)
267277

268278
if test.wantErr {

pkg/handler/rename/rename.go

+21-10
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,49 @@ import (
99
"github.com/tomMoulard/htransformation/pkg/utils/header"
1010
)
1111

12-
func Validate(rule types.Rule) error {
13-
if _, err := regexp.Compile(rule.Header); err != nil {
14-
return fmt.Errorf("%s: %w", types.ErrInvalidRegexp.Error(), err)
12+
type Rename struct {
13+
rule *types.Rule
14+
}
15+
16+
func New(rule types.Rule) (types.Handler, error) {
17+
re, err := regexp.Compile(rule.Header)
18+
if err != nil {
19+
return nil, fmt.Errorf("%w: %s", types.ErrInvalidRegexp, rule.Name)
1520
}
1621

17-
if rule.Value == "" {
22+
rule.Regexp = re
23+
24+
return &Rename{rule: &rule}, nil
25+
}
26+
27+
func (r *Rename) Validate() error {
28+
if r.rule.Value == "" {
1829
return types.ErrMissingRequiredFields
1930
}
2031

2132
return nil
2233
}
2334

24-
func Handle(rw http.ResponseWriter, req *http.Request, rule types.Rule) {
35+
func (r *Rename) Handle(rw http.ResponseWriter, req *http.Request) {
2536
originalHost := req.Header.Get("Host") // Eventually X-Forwarded-Host
2637
req.Header.Set("Host", req.Host)
2738

2839
for headerName, headerValues := range req.Header {
29-
if matched := rule.Regexp.Match([]byte(headerName)); !matched {
40+
if matched := r.rule.Regexp.Match([]byte(headerName)); !matched {
3041
continue
3142
}
3243

33-
if rule.SetOnResponse {
44+
if r.rule.SetOnResponse {
3445
rw.Header().Del(headerName)
3546
} else {
3647
header.Delete(req, headerName)
3748
}
3849

3950
for _, val := range headerValues {
40-
if rule.SetOnResponse {
41-
rw.Header().Set(rule.Value, val)
51+
if r.rule.SetOnResponse {
52+
rw.Header().Set(r.rule.Value, val)
4253
} else {
43-
header.Set(req, rule.Value, val)
54+
header.Set(req, r.rule.Value, val)
4455
}
4556
}
4657
}

0 commit comments

Comments
 (0)