Skip to content

Commit fb63e2e

Browse files
caddyhttp: New experimental handler for intercepting responses (#6232)
* feat: add generic response interceptors * fix: cs * rename intercept * add some docs * @francislavoie review (first round) * Update modules/caddyhttp/intercept/intercept.go Co-authored-by: Francis Lavoie <[email protected]> * shorthands: ir to resp * mark exported symbols as experimental --------- Co-authored-by: Francis Lavoie <[email protected]>
1 parent 583c585 commit fb63e2e

File tree

6 files changed

+617
-0
lines changed

6 files changed

+617
-0
lines changed

caddyconfig/httpcaddyfile/directives.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ var defaultDirectiveOrder = []string{
7474
"request_header",
7575
"encode",
7676
"push",
77+
"intercept",
7778
"templates",
7879

7980
// special routing & dispatching directives

caddyconfig/httpcaddyfile/shorthands.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func NewShorthandReplacer() ShorthandReplacer {
3636
{regexp.MustCompile(`{re\.([\w-\.]*)}`), "{http.regexp.$1}"},
3737
{regexp.MustCompile(`{vars\.([\w-]*)}`), "{http.vars.$1}"},
3838
{regexp.MustCompile(`{rp\.([\w-\.]*)}`), "{http.reverse_proxy.$1}"},
39+
{regexp.MustCompile(`{resp\.([\w-\.]*)}`), "{http.intercept.$1}"},
3940
{regexp.MustCompile(`{err\.([\w-\.]*)}`), "{http.error.$1}"},
4041
{regexp.MustCompile(`{file_match\.([\w-]*)}`), "{http.matchers.file.$1}"},
4142
}
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
localhost
2+
3+
respond "To intercept"
4+
5+
intercept {
6+
@500 status 500
7+
replace_status @500 400
8+
9+
@all status 2xx 3xx 4xx 5xx
10+
replace_status @all {http.error.status_code}
11+
12+
replace_status {http.error.status_code}
13+
14+
@accel header X-Accel-Redirect *
15+
handle_response @accel {
16+
respond "Header X-Accel-Redirect!"
17+
}
18+
19+
@another {
20+
header X-Another *
21+
}
22+
handle_response @another {
23+
respond "Header X-Another!"
24+
}
25+
26+
@401 status 401
27+
handle_response @401 {
28+
respond "Status 401!"
29+
}
30+
31+
handle_response {
32+
respond "Any! This should be last in the JSON!"
33+
}
34+
35+
@403 {
36+
status 403
37+
}
38+
handle_response @403 {
39+
respond "Status 403!"
40+
}
41+
42+
@multi {
43+
status 401 403
44+
status 404
45+
header Foo *
46+
header Bar *
47+
}
48+
handle_response @multi {
49+
respond "Headers Foo, Bar AND statuses 401, 403 and 404!"
50+
}
51+
}
52+
----------
53+
{
54+
"apps": {
55+
"http": {
56+
"servers": {
57+
"srv0": {
58+
"listen": [
59+
":443"
60+
],
61+
"routes": [
62+
{
63+
"match": [
64+
{
65+
"host": [
66+
"localhost"
67+
]
68+
}
69+
],
70+
"handle": [
71+
{
72+
"handler": "subroute",
73+
"routes": [
74+
{
75+
"handle": [
76+
{
77+
"handle_response": [
78+
{
79+
"match": {
80+
"status_code": [
81+
500
82+
]
83+
},
84+
"status_code": 400
85+
},
86+
{
87+
"match": {
88+
"status_code": [
89+
2,
90+
3,
91+
4,
92+
5
93+
]
94+
},
95+
"status_code": "{http.error.status_code}"
96+
},
97+
{
98+
"match": {
99+
"headers": {
100+
"X-Accel-Redirect": [
101+
"*"
102+
]
103+
}
104+
},
105+
"routes": [
106+
{
107+
"handle": [
108+
{
109+
"body": "Header X-Accel-Redirect!",
110+
"handler": "static_response"
111+
}
112+
]
113+
}
114+
]
115+
},
116+
{
117+
"match": {
118+
"headers": {
119+
"X-Another": [
120+
"*"
121+
]
122+
}
123+
},
124+
"routes": [
125+
{
126+
"handle": [
127+
{
128+
"body": "Header X-Another!",
129+
"handler": "static_response"
130+
}
131+
]
132+
}
133+
]
134+
},
135+
{
136+
"match": {
137+
"status_code": [
138+
401
139+
]
140+
},
141+
"routes": [
142+
{
143+
"handle": [
144+
{
145+
"body": "Status 401!",
146+
"handler": "static_response"
147+
}
148+
]
149+
}
150+
]
151+
},
152+
{
153+
"match": {
154+
"status_code": [
155+
403
156+
]
157+
},
158+
"routes": [
159+
{
160+
"handle": [
161+
{
162+
"body": "Status 403!",
163+
"handler": "static_response"
164+
}
165+
]
166+
}
167+
]
168+
},
169+
{
170+
"match": {
171+
"headers": {
172+
"Bar": [
173+
"*"
174+
],
175+
"Foo": [
176+
"*"
177+
]
178+
},
179+
"status_code": [
180+
401,
181+
403,
182+
404
183+
]
184+
},
185+
"routes": [
186+
{
187+
"handle": [
188+
{
189+
"body": "Headers Foo, Bar AND statuses 401, 403 and 404!",
190+
"handler": "static_response"
191+
}
192+
]
193+
}
194+
]
195+
},
196+
{
197+
"status_code": "{http.error.status_code}"
198+
},
199+
{
200+
"routes": [
201+
{
202+
"handle": [
203+
{
204+
"body": "Any! This should be last in the JSON!",
205+
"handler": "static_response"
206+
}
207+
]
208+
}
209+
]
210+
}
211+
],
212+
"handler": "intercept"
213+
},
214+
{
215+
"body": "To intercept",
216+
"handler": "static_response"
217+
}
218+
]
219+
}
220+
]
221+
}
222+
],
223+
"terminal": true
224+
}
225+
]
226+
}
227+
}
228+
}
229+
}
230+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package integration
2+
3+
import (
4+
"testing"
5+
6+
"github.com/caddyserver/caddy/v2/caddytest"
7+
)
8+
9+
func TestIntercept(t *testing.T) {
10+
tester := caddytest.NewTester(t)
11+
tester.InitServer(`{
12+
skip_install_trust
13+
admin localhost:2999
14+
http_port 9080
15+
https_port 9443
16+
grace_period 1ns
17+
}
18+
19+
localhost:9080 {
20+
respond /intercept "I'm a teapot" 408
21+
respond /no-intercept "I'm not a teapot"
22+
23+
intercept {
24+
@teapot status 408
25+
handle_response @teapot {
26+
respond /intercept "I'm a combined coffee/tea pot that is temporarily out of coffee" 503
27+
}
28+
}
29+
}
30+
`, "caddyfile")
31+
32+
tester.AssertGetResponse("http://localhost:9080/intercept", 503, "I'm a combined coffee/tea pot that is temporarily out of coffee")
33+
tester.AssertGetResponse("http://localhost:9080/no-intercept", 200, "I'm not a teapot")
34+
}

0 commit comments

Comments
 (0)