Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 12, 14:30 UTC
rewrite: `uri query` replace operation (#6165)
* Implemented query replace oeration * Modified replace operation to use regexes in caddyfile * Added more tests to uri query operations
Aziz Rmadi committed
Mar 21, 2024 at 21:23 UTC
29f57faa8679344fa40ea9b317d724f0604d5b40
4 files changed
+263
-1
caddytest/integration/caddyfile_adapt/uri_query_operations.caddyfiletest
new
+106
@@ -0,0 +1,106 @@
1
+:9080
2
+uri query +foo bar
3
+uri query -baz
4
+uri query taz test
5
+uri query key=value example
6
+uri query changethis>changed
7
+uri query {
8
+ findme value replacement
9
+ +foo1 baz
10
+}
11
+
12
+respond "{query}"
13
+----------
14
+{
15
+ "apps": {
16
+ "http": {
17
+ "servers": {
18
+ "srv0": {
19
+ "listen": [
20
+ ":9080"
21
+ ],
22
+ "routes": [
23
+ {
24
+ "handle": [
25
+ {
26
+ "handler": "rewrite",
27
+ "query": {
28
+ "add": [
29
+ {
30
+ "key": "foo",
31
+ "val": "bar"
32
+ }
33
+ ]
34
+ }
35
+ },
36
+ {
37
+ "handler": "rewrite",
38
+ "query": {
39
+ "delete": [
40
+ "baz"
41
+ ]
42
+ }
43
+ },
44
+ {
45
+ "handler": "rewrite",
46
+ "query": {
47
+ "set": [
48
+ {
49
+ "key": "taz",
50
+ "val": "test"
51
+ }
52
+ ]
53
+ }
54
+ },
55
+ {
56
+ "handler": "rewrite",
57
+ "query": {
58
+ "set": [
59
+ {
60
+ "key": "key=value",
61
+ "val": "example"
62
+ }
63
+ ]
64
+ }
65
+ },
66
+ {
67
+ "handler": "rewrite",
68
+ "query": {
69
+ "rename": [
70
+ {
71
+ "key": "changethis",
72
+ "val": "changed"
73
+ }
74
+ ]
75
+ }
76
+ },
77
+ {
78
+ "handler": "rewrite",
79
+ "query": {
80
+ "add": [
81
+ {
82
+ "key": "foo1",
83
+ "val": "baz"
84
+ }
85
+ ],
86
+ "replace": [
87
+ {
88
+ "key": "findme",
89
+ "replace": "replacement",
90
+ "search_regexp": "value"
91
+ }
92
+ ]
93
+ }
94
+ },
95
+ {
96
+ "body": "{http.request.uri.query}",
97
+ "handler": "static_response"
98
+ }
99
+ ]
100
+ }
101
+ ]
102
+ }
103
+ }
104
+ }
105
+ }
106
+}
\ No newline at end of file
caddytest/integration/caddyfile_test.go
+87
@@ -569,6 +569,93 @@ func TestRenameAndOtherOps(t *testing.T) {
569
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=taz&bar=baz")
570
}
571
572
+func TestReplaceOps(t *testing.T) {
573
+ tester := caddytest.NewTester(t)
574
+
575
+ tester.InitServer(`
576
+ {
577
+ admin localhost:2999
578
+ http_port 9080
579
+ }
580
+ :9080
581
+ uri query foo bar baz
582
+ respond "{query}"`, "caddyfile")
583
+
584
+ tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
585
+}
586
+
587
+func TestReplaceWithReplacementPlaceholder(t *testing.T) {
588
+ tester := caddytest.NewTester(t)
589
+ tester.InitServer(`
590
+ {
591
+ admin localhost:2999
592
+ http_port 9080
593
+ }
594
+ :9080
595
+ uri query foo bar {query.placeholder}
596
+ respond "{query}"`, "caddyfile")
597
+
598
+ tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
599
+
600
+}
601
+
602
+func TestReplaceWithKeyPlaceholder(t *testing.T) {
603
+ tester := caddytest.NewTester(t)
604
+ tester.InitServer(`
605
+ {
606
+ admin localhost:2999
607
+ http_port 9080
608
+ }
609
+ :9080
610
+ uri query {query.placeholder} bar baz
611
+ respond "{query}"`, "caddyfile")
612
+
613
+ tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=foo&foo=bar", 200, "foo=baz&placeholder=foo")
614
+}
615
+
616
+func TestPartialReplacement(t *testing.T) {
617
+ tester := caddytest.NewTester(t)
618
+ tester.InitServer(`
619
+ {
620
+ admin localhost:2999
621
+ http_port 9080
622
+ }
623
+ :9080
624
+ uri query foo ar az
625
+ respond "{query}"`, "caddyfile")
626
+
627
+ tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
628
+}
629
+
630
+func TestNonExistingSearch(t *testing.T) {
631
+ tester := caddytest.NewTester(t)
632
+ tester.InitServer(`
633
+ {
634
+ admin localhost:2999
635
+ http_port 9080
636
+ }
637
+ :9080
638
+ uri query foo var baz
639
+ respond "{query}"`, "caddyfile")
640
+
641
+ tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=bar")
642
+}
643
+
644
+func TestReplaceAllOps(t *testing.T) {
645
+ tester := caddytest.NewTester(t)
646
+
647
+ tester.InitServer(`
648
+ {
649
+ admin localhost:2999
650
+ http_port 9080
651
+ }
652
+ :9080
653
+ uri query * bar baz
654
+ respond "{query}"`, "caddyfile")
655
+
656
+ tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar&baz=bar", 200, "baz=baz&foo=baz")
657
+}
658
+
659
func TestUriOpsBlock(t *testing.T) {
660
tester := caddytest.NewTester(t)
661
modules/caddyhttp/rewrite/caddyfile.go
+3
@@ -213,6 +213,9 @@ func applyQueryOps(h httpcaddyfile.Helper, qo *queryOps, args []string) error {
213
renameValKey := strings.Split(key, ">")
214
qo.Rename = append(qo.Rename, queryOpsArguments{Key: renameValKey[0], Val: renameValKey[1]})
215
216
+ case len(args) == 3:
217
+ qo.Replace = append(qo.Replace, &queryOpsReplacement{Key: key, SearchRegexp: args[1], Replace: args[2]})
218
+
219
default:
220
if len(args) != 2 {
221
return h.ArgErr()
modules/caddyhttp/rewrite/rewrite.go
+67
-1
@@ -118,6 +118,12 @@ func (rewr *Rewrite) Provision(ctx caddy.Context) error {
118
rep.re = re
119
}
120
121
+ for _, replacementOp := range rewr.Query.Replace {
122
+ err := replacementOp.Provision(ctx)
123
+ if err != nil {
124
+ return fmt.Errorf("compiling regular expression %s in query rewrite replace operation: %v", replacementOp.SearchRegexp, err)
125
+ }
126
+ }
127
return nil
128
}
129
@@ -490,13 +496,27 @@ type queryOps struct {
496
// and only appends an additional value for that key if any already exist.
497
Add []queryOpsArguments `json:"add,omitempty"`
498
499
+ // Replaces query parameters.
500
+ Replace []*queryOpsReplacement `json:"replace,omitempty"`
501
+
502
// Deletes a given query key by name.
503
Delete []string `json:"delete,omitempty"`
504
}
505
506
+// Provision compiles the query replace operation regex.
507
+func (replacement *queryOpsReplacement) Provision(_ caddy.Context) error {
508
+ if replacement.SearchRegexp != "" {
509
+ re, err := regexp.Compile(replacement.SearchRegexp)
510
+ if err != nil {
511
+ return fmt.Errorf("replacement for query field '%s': %v", replacement.Key, err)
512
+ }
513
+ replacement.re = re
514
+ }
515
+ return nil
516
+}
517
+
518
func (q *queryOps) do(r *http.Request, repl *caddy.Replacer) {
519
query := r.URL.Query()
499
-
520
for _, renameParam := range q.Rename {
521
key := repl.ReplaceAll(renameParam.Key, "")
522
val := repl.ReplaceAll(renameParam.Val, "")
@@ -525,6 +545,36 @@ func (q *queryOps) do(r *http.Request, repl *caddy.Replacer) {
545
query[key] = append(query[key], val)
546
}
547
548
+ for _, replaceParam := range q.Replace {
549
+ key := repl.ReplaceAll(replaceParam.Key, "")
550
+ search := repl.ReplaceKnown(replaceParam.Search, "")
551
+ replace := repl.ReplaceKnown(replaceParam.Replace, "")
552
+
553
+ // replace all query keys...
554
+ if key == "*" {
555
+ for fieldName, vals := range query {
556
+ for i := range vals {
557
+ if replaceParam.re != nil {
558
+ query[fieldName][i] = replaceParam.re.ReplaceAllString(query[fieldName][i], replace)
559
+ } else {
560
+ query[fieldName][i] = strings.ReplaceAll(query[fieldName][i], search, replace)
561
+ }
562
+ }
563
+ }
564
+ continue
565
+ }
566
+
567
+ for fieldName, vals := range query {
568
+ for i := range vals {
569
+ if replaceParam.re != nil {
570
+ query[fieldName][i] = replaceParam.re.ReplaceAllString(query[fieldName][i], replace)
571
+ } else {
572
+ query[fieldName][i] = strings.ReplaceAll(query[fieldName][i], search, replace)
573
+ }
574
+ }
575
+ }
576
+ }
577
+
578
for _, deleteParam := range q.Delete {
579
param := repl.ReplaceAll(deleteParam, "")
580
if param == "" {
@@ -546,5 +596,21 @@ type queryOpsArguments struct {
596
Val string `json:"val,omitempty"`
597
}
598
599
+type queryOpsReplacement struct {
600
+ // The key to replace in the query string.
601
+ Key string `json:"key,omitempty"`
602
+
603
+ // The substring to search for.
604
+ Search string `json:"search,omitempty"`
605
+
606
+ // The regular expression to search with.
607
+ SearchRegexp string `json:"search_regexp,omitempty"`
608
+
609
+ // The string with which to replace matches.
610
+ Replace string `json:"replace,omitempty"`
611
+
612
+ re *regexp.Regexp
613
+}
614
+
615
// Interface guard
616
var _ caddyhttp.MiddlewareHandler = (*Rewrite)(nil)