Skip to content

Commit 2c18988

Browse files
committed
echocat#20 Added path_content_type_combination parameter to make it possible to decide if the user wants and/or and set it by default to and.
1 parent 4758934 commit 2c18988

File tree

5 files changed

+137
-18
lines changed

5 files changed

+137
-18
lines changed

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ This could be useful to modify static HTML files to add (for example) Google Ana
1919

2020
```
2121
filter rule {
22-
path <regexp pattern>
23-
content_type <regexp pattern>
24-
search_pattern <regexp pattern>
25-
replacement <replacement pattern>
22+
path <regexp pattern>
23+
content_type <regexp pattern>
24+
path_content_type_combination <and|or>
25+
search_pattern <regexp pattern>
26+
replacement <replacement pattern>
2627
}
2728
filter rule ...
2829
filter max_buffer_size <maximum buffer size in bytes>
@@ -31,8 +32,11 @@ filter max_buffer_size <maximum buffer size in bytes>
3132
* **rule**: Defines a new filter rule for a file to respond.
3233
> **Important:** Define ``path`` and/or ``content_type`` not to open. Slack rules could dramatically impact the system performance because every response is recorded to memory before returning it.
3334
35+
> **Hint:** Since version 0.8 are ``path`` and ``content_type`` are `and` combined if both provided. Before it was `or` combined but this was an unexpected
36+
3437
* **path**: Regular expression that matches the requested path.
3538
* **content_type**: Regular expression that matches the requested content type that results after the evaluation of the whole request.
39+
* **path_content_type_combination**: _(Since 0.8)_ Could be `and` or `or`. (Default: `and` - before this parameter existed it was `or`)
3640
* **search_pattern**: Regular expression to find in the response body to replace it.
3741
* **replacement**: Pattern to replace the ``search_pattern`` with.
3842
<br>You can use parameters. Each parameter must be formatted like: ``{name}``.

init.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,16 @@ func evalRule(controller *caddy.Controller, args []string, target *filterHandler
8585
return controller.Errf("No more arguments for filter block 'rule' supported.")
8686
}
8787
targetRule := new(rule)
88+
targetRule.pathAndContentTypeCombination = pathAndContentTypeAndCombination
8889
for controller.NextBlock() {
8990
optionName := controller.Val()
9091
switch optionName {
9192
case "path":
9293
err = evalPath(controller, targetRule)
9394
case "content_type":
9495
err = evalContentType(controller, targetRule)
96+
case "path_content_type_combination":
97+
err = evalPathAndContentTypeCombination(controller, targetRule)
9598
case "search_pattern":
9699
err = evalSearchPattern(controller, targetRule)
97100
case "replacement":
@@ -127,6 +130,18 @@ func evalContentType(controller *caddy.Controller, target *rule) error {
127130
})
128131
}
129132

133+
func evalPathAndContentTypeCombination(controller *caddy.Controller, target *rule) error {
134+
return evalSimpleOption(controller, func(plainValue string) error {
135+
for _, candidate := range possiblePathAndContentTypeCombination {
136+
if string(candidate) == plainValue {
137+
target.pathAndContentTypeCombination = candidate
138+
return nil
139+
}
140+
}
141+
return controller.Errf("Illegal value for 'path_content_type_combination': %v", plainValue)
142+
})
143+
}
144+
130145
func evalSearchPattern(controller *caddy.Controller, target *rule) error {
131146
return evalRegexpOption(controller, func(value *regexp.Regexp) error {
132147
target.searchPattern = value

init_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,42 @@ func (s *initTest) Test_setup(c *C) {
2828
r := handler.rules[0]
2929
c.Assert(r.path.String(), Equals, "myPath")
3030
c.Assert(r.contentType.String(), Equals, "myContentType")
31+
c.Assert(r.pathAndContentTypeCombination, Equals, pathAndContentTypeAndCombination)
3132
c.Assert(r.searchPattern.String(), Equals, "mySearchPattern")
3233
c.Assert(string(r.replacement), Equals, "myReplacement")
3334
}
3435

36+
func (s *initTest) Test_parseConfiguration_withPathAndContentTypeCombination(c *C) {
37+
controller := s.newControllerFor("filter rule {\npath myPath\ncontent_type myContentType\nsearch_pattern mySearchPattern\nreplacement myReplacement\n}\n")
38+
err := setup(controller)
39+
c.Assert(err, IsNil)
40+
handler, ok := httpserver.GetConfig(controller).Middleware()[0](newMockHandler("moo", 200)).(*filterHandler)
41+
c.Assert(ok, Equals, true)
42+
c.Assert(len(handler.rules), Equals, 1)
43+
c.Assert(handler.rules[0].pathAndContentTypeCombination, Equals, pathAndContentTypeAndCombination)
44+
45+
controller = s.newControllerFor("filter rule {\npath_content_type_combination or\npath myPath\ncontent_type myContentType\nsearch_pattern mySearchPattern\n}\n")
46+
err = setup(controller)
47+
c.Assert(err, IsNil)
48+
handler, ok = httpserver.GetConfig(controller).Middleware()[0](newMockHandler("moo", 200)).(*filterHandler)
49+
c.Assert(ok, Equals, true)
50+
c.Assert(len(handler.rules), Equals, 1)
51+
c.Assert(handler.rules[0].pathAndContentTypeCombination, Equals, pathAndContentTypeOrCombination)
52+
53+
controller = s.newControllerFor("filter rule {\npath_content_type_combination and\npath myPath\ncontent_type myContentType\nsearch_pattern mySearchPattern\n}\n")
54+
err = setup(controller)
55+
c.Assert(err, IsNil)
56+
handler, ok = httpserver.GetConfig(controller).Middleware()[0](newMockHandler("moo", 200)).(*filterHandler)
57+
c.Assert(ok, Equals, true)
58+
c.Assert(len(handler.rules), Equals, 1)
59+
c.Assert(handler.rules[0].pathAndContentTypeCombination, Equals, pathAndContentTypeAndCombination)
60+
61+
controller = s.newControllerFor("filter rule {\npath_content_type_combination foo\npath myPath\ncontent_type myContentType\nsearch_pattern mySearchPattern\n}\n")
62+
err = setup(controller)
63+
c.Assert(err, NotNil)
64+
c.Assert(err.Error(), Equals, "Testfile:2 - Parse error: Illegal value for 'path_content_type_combination': foo")
65+
}
66+
3567
func (s *initTest) Test_parseConfiguration_directNamed(c *C) {
3668
handler, err := parseConfiguration(s.newControllerFor("filter rule {\npath myPath\ncontent_type myContentType\nsearch_pattern mySearchPattern\nreplacement myReplacement\n}\n"))
3769
c.Assert(err, IsNil)

rule.go

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,57 @@ import (
66
)
77

88
type rule struct {
9-
path *regexp.Regexp
10-
contentType *regexp.Regexp
11-
searchPattern *regexp.Regexp
12-
replacement []byte
9+
path *regexp.Regexp
10+
contentType *regexp.Regexp
11+
pathAndContentTypeCombination pathAndContentTypeCombination
12+
searchPattern *regexp.Regexp
13+
replacement []byte
1314
}
1415

15-
func (instance *rule) matches(request *http.Request, responseHeader *http.Header) bool {
16-
if instance.path != nil && request != nil && instance.path.MatchString(request.URL.Path) {
17-
return true
16+
type pathAndContentTypeCombination string
17+
18+
const (
19+
pathAndContentTypeAndCombination = pathAndContentTypeCombination("and")
20+
pathAndContentTypeOrCombination = pathAndContentTypeCombination("or")
21+
)
22+
23+
var possiblePathAndContentTypeCombination = []pathAndContentTypeCombination{
24+
pathAndContentTypeAndCombination,
25+
pathAndContentTypeOrCombination,
26+
}
27+
28+
func (instance rule) evaluatePathAndContentTypeResult(pathMatch bool, contentTypeMatch bool) bool {
29+
combination := instance.pathAndContentTypeCombination
30+
if combination == pathAndContentTypeCombination("") {
31+
combination = pathAndContentTypeAndCombination
1832
}
19-
if instance.contentType != nil && responseHeader != nil && instance.contentType.MatchString(responseHeader.Get("Content-Type")) {
20-
return true
33+
if combination == pathAndContentTypeAndCombination {
34+
return pathMatch && contentTypeMatch
35+
}
36+
if combination == pathAndContentTypeOrCombination {
37+
return pathMatch || contentTypeMatch
2138
}
2239
return false
2340
}
2441

42+
func (instance *rule) matches(request *http.Request, responseHeader *http.Header) bool {
43+
var pathMatch, contentTypeMatch bool
44+
45+
if instance.path != nil {
46+
pathMatch = request != nil && instance.path.MatchString(request.URL.Path)
47+
} else {
48+
pathMatch = true
49+
}
50+
51+
if instance.contentType != nil {
52+
contentTypeMatch = responseHeader != nil && instance.contentType.MatchString(responseHeader.Get("Content-Type"))
53+
} else {
54+
contentTypeMatch = true
55+
}
56+
57+
return instance.evaluatePathAndContentTypeResult(pathMatch, contentTypeMatch)
58+
}
59+
2560
func (instance *rule) execute(request *http.Request, responseHeader *http.Header, input []byte) []byte {
2661
pattern := instance.searchPattern
2762
if pattern == nil {

rule_test.go

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,54 @@ func (s *ruleTest) Test_matches_contentType(c *C) {
4747
c.Assert(r.matches(nil, &header), Equals, false)
4848
}
4949

50-
func (s *ruleTest) Test_matches_combined(c *C) {
50+
func (s *ruleTest) Test_matches_and_combined(c *C) {
5151
req := &http.Request{}
5252
header := http.Header{}
5353
r := &rule{
54-
path: regexp.MustCompile(".*\\.html"),
55-
contentType: regexp.MustCompile("text/html.*"),
54+
path: regexp.MustCompile(".*\\.html"),
55+
contentType: regexp.MustCompile("text/html.*"),
56+
pathAndContentTypeCombination: pathAndContentTypeAndCombination,
5657
}
5758

5859
req.URL = testUrl1
5960
header.Set("Content-Type", "text/html")
6061
c.Assert(r.matches(req, &header), Equals, true)
62+
c.Assert(r.matches(nil, &header), Equals, false)
63+
c.Assert(r.matches(req, nil), Equals, false)
6164

6265
req.URL = testUrl2
63-
header.Del("Content-Type")
64-
c.Assert(r.matches(nil, &header), Equals, false)
66+
c.Assert(r.matches(req, &header), Equals, false)
67+
68+
req.URL = testUrl1
69+
header.Set("Content-Type", "text/plain")
70+
c.Assert(r.matches(req, &header), Equals, false)
71+
}
72+
73+
func (s *ruleTest) Test_matches_or_combined(c *C) {
74+
req := &http.Request{}
75+
header := http.Header{}
76+
r := &rule{
77+
path: regexp.MustCompile(".*\\.html"),
78+
contentType: regexp.MustCompile("text/html.*"),
79+
pathAndContentTypeCombination: pathAndContentTypeOrCombination,
80+
}
81+
82+
req.URL = testUrl1
83+
header.Set("Content-Type", "text/html")
84+
c.Assert(r.matches(req, &header), Equals, true)
85+
c.Assert(r.matches(nil, &header), Equals, true)
86+
c.Assert(r.matches(req, nil), Equals, true)
87+
c.Assert(r.matches(nil, nil), Equals, false)
88+
89+
req.URL = testUrl2
90+
c.Assert(r.matches(req, &header), Equals, true)
91+
92+
req.URL = testUrl1
93+
header.Set("Content-Type", "text/plain")
94+
c.Assert(r.matches(req, &header), Equals, true)
95+
96+
req.URL = testUrl2
97+
c.Assert(r.matches(req, &header), Equals, false)
6598
}
6699

67100
func (s *ruleTest) Test_execute(c *C) {

0 commit comments

Comments
 (0)