Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 13, 05:30 UTC
reverseproxy: refactor HTTP transport layer (#5369)
Co-authored-by: Francis Lavoie <lavofr@gmail.com> Co-authored-by: Weidi Deng <weidi_deng@icloud.com>
Mohammed Al Sahaf committed
Feb 24, 2023 at 22:54 UTC
e3909cc385f68a636cc8675dc12f6cd5185d722d
2 files changed
+148
-140
caddytest/integration/reverseproxy_test.go
+134
-134
@@ -22,39 +22,39 @@ func TestSRVReverseProxy(t *testing.T) {
22
},
23
"apps": {
24
"pki": {
25
- "certificate_authorities" : {
26
- "local" : {
27
- "install_trust": false
25
+ "certificate_authorities": {
26
+ "local": {
27
+ "install_trust": false
28
}
29
}
30
},
31
- "http": {
32
- "grace_period": 1,
33
- "servers": {
34
- "srv0": {
35
- "listen": [
36
- ":8080"
37
- ],
38
- "routes": [
39
- {
40
- "handle": [
41
- {
42
- "handler": "reverse_proxy",
43
- "upstreams": [
44
- {
45
- "lookup_srv": "srv.host.service.consul"
46
- }
31
+ "http": {
32
+ "grace_period": 1,
33
+ "servers": {
34
+ "srv0": {
35
+ "listen": [
36
+ ":18080"
37
+ ],
38
+ "routes": [
39
+ {
40
+ "handle": [
41
+ {
42
+ "handler": "reverse_proxy",
43
+ "upstreams": [
44
+ {
45
+ "lookup_srv": "srv.host.service.consul"
46
+ }
47
+ ]
48
+ }
49
+ ]
50
+ }
51
]
48
- }
49
- ]
50
- }
51
- ]
52
- }
52
+ }
53
+ }
54
}
54
- }
55
}
56
- }
57
- `, "json")
56
+ }
57
+ `, "json")
58
}
59
60
func TestSRVWithDial(t *testing.T) {
@@ -62,39 +62,39 @@ func TestSRVWithDial(t *testing.T) {
62
{
63
"apps": {
64
"pki": {
65
- "certificate_authorities" : {
66
- "local" : {
67
- "install_trust": false
68
- }
65
+ "certificate_authorities": {
66
+ "local": {
67
+ "install_trust": false
68
+ }
69
}
70
- },
71
- "http": {
72
- "grace_period": 1,
73
- "servers": {
74
- "srv0": {
75
- "listen": [
76
- ":8080"
77
- ],
78
- "routes": [
79
- {
80
- "handle": [
81
- {
82
- "handler": "reverse_proxy",
83
- "upstreams": [
84
- {
85
- "dial": "tcp/address.to.upstream:80",
86
- "lookup_srv": "srv.host.service.consul"
87
- }
70
+ },
71
+ "http": {
72
+ "grace_period": 1,
73
+ "servers": {
74
+ "srv0": {
75
+ "listen": [
76
+ ":18080"
77
+ ],
78
+ "routes": [
79
+ {
80
+ "handle": [
81
+ {
82
+ "handler": "reverse_proxy",
83
+ "upstreams": [
84
+ {
85
+ "dial": "tcp/address.to.upstream:80",
86
+ "lookup_srv": "srv.host.service.consul"
87
+ }
88
+ ]
89
+ }
90
+ ]
91
+ }
92
]
89
- }
90
- ]
91
- }
92
- ]
93
- }
93
+ }
94
+ }
95
}
95
- }
96
}
97
- }
97
+ }
98
`, "json", `upstream: specifying dial address is incompatible with lookup_srv: 0: {\"dial\": \"tcp/address.to.upstream:80\", \"lookup_srv\": \"srv.host.service.consul\"}`)
99
}
100
@@ -138,41 +138,41 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
138
},
139
"apps": {
140
"pki": {
141
- "certificate_authorities" : {
142
- "local" : {
143
- "install_trust": false
144
- }
141
+ "certificate_authorities": {
142
+ "local": {
143
+ "install_trust": false
144
+ }
145
}
146
- },
147
- "http": {
148
- "grace_period": 1,
149
- "servers": {
150
- "srv0": {
151
- "listen": [
152
- ":8080"
153
- ],
154
- "routes": [
155
- {
156
- "handle": [
157
- {
158
- "handler": "reverse_proxy",
159
- "upstreams": [
160
- {
161
- "dial": "unix/{http.request.header.X-Caddy-Upstream-Dial}"
162
- }
146
+ },
147
+ "http": {
148
+ "grace_period": 1,
149
+ "servers": {
150
+ "srv0": {
151
+ "listen": [
152
+ ":18080"
153
+ ],
154
+ "routes": [
155
+ {
156
+ "handle": [
157
+ {
158
+ "handler": "reverse_proxy",
159
+ "upstreams": [
160
+ {
161
+ "dial": "unix/{http.request.header.X-Caddy-Upstream-Dial}"
162
+ }
163
+ ]
164
+ }
165
+ ]
166
+ }
167
]
164
- }
165
- ]
166
- }
167
- ]
168
- }
169
- }
170
- }
168
+ }
169
+ }
170
+ }
171
}
172
- }
172
+ }
173
`, "json")
174
175
- req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil)
175
+ req, err := http.NewRequest(http.MethodGet, "http://localhost:18080", nil)
176
if err != nil {
177
t.Fail()
178
return
@@ -190,18 +190,18 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
190
},
191
"apps": {
192
"pki": {
193
- "certificate_authorities" : {
194
- "local" : {
195
- "install_trust": false
196
- }
193
+ "certificate_authorities": {
194
+ "local": {
195
+ "install_trust": false
196
+ }
197
}
198
- },
198
+ },
199
"http": {
200
"grace_period": 1,
201
"servers": {
202
"srv0": {
203
"listen": [
204
- ":8080"
204
+ ":18080"
205
],
206
"routes": [
207
{
@@ -264,14 +264,14 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
264
}
265
}
266
}
267
- `, "json")
267
+ `, "json")
268
269
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
270
if err != nil {
271
t.Fail()
272
return
273
}
274
- req.Header.Set("X-Caddy-Upstream-Dial", "localhost:8080")
274
+ req.Header.Set("X-Caddy-Upstream-Dial", "localhost:18080")
275
tester.AssertResponse(req, 200, "Hello, World!")
276
}
277
@@ -284,18 +284,18 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
284
},
285
"apps": {
286
"pki": {
287
- "certificate_authorities" : {
288
- "local" : {
289
- "install_trust": false
290
- }
287
+ "certificate_authorities": {
288
+ "local": {
289
+ "install_trust": false
290
+ }
291
}
292
- },
292
+ },
293
"http": {
294
"grace_period": 1,
295
"servers": {
296
"srv0": {
297
"listen": [
298
- ":8080"
298
+ ":18080"
299
],
300
"routes": [
301
{
@@ -340,7 +340,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
340
"handler": "reverse_proxy",
341
"upstreams": [
342
{
343
- "dial": "tcp/{http.request.header.X-Caddy-Upstream-Dial}:8080"
343
+ "dial": "tcp/{http.request.header.X-Caddy-Upstream-Dial}:18080"
344
}
345
]
346
}
@@ -358,7 +358,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
358
}
359
}
360
}
361
- `, "json")
361
+ `, "json")
362
363
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
364
if err != nil {
@@ -375,42 +375,42 @@ func TestSRVWithActiveHealthcheck(t *testing.T) {
375
"apps": {
376
"pki": {
377
"certificate_authorities" : {
378
- "local" : {
379
- "install_trust": false
380
- }
378
+ "local" : {
379
+ "install_trust": false
380
+ }
381
}
382
- },
383
- "http": {
384
- "grace_period": 1,
385
- "servers": {
386
- "srv0": {
387
- "listen": [
388
- ":8080"
389
- ],
390
- "routes": [
391
- {
392
- "handle": [
393
- {
394
- "handler": "reverse_proxy",
395
- "health_checks": {
396
- "active": {
397
- "path": "/ok"
382
+ },
383
+ "http": {
384
+ "grace_period": 1,
385
+ "servers": {
386
+ "srv0": {
387
+ "listen": [
388
+ ":18080"
389
+ ],
390
+ "routes": [
391
+ {
392
+ "handle": [
393
+ {
394
+ "handler": "reverse_proxy",
395
+ "health_checks": {
396
+ "active": {
397
+ "path": "/ok"
398
+ }
399
+ },
400
+ "upstreams": [
401
+ {
402
+ "lookup_srv": "srv.host.service.consul"
403
+ }
404
+ ]
405
+ }
406
+ ]
407
}
399
- },
400
- "upstreams": [
401
- {
402
- "lookup_srv": "srv.host.service.consul"
403
- }
408
]
405
- }
406
- ]
407
- }
408
- ]
409
- }
409
+ }
410
+ }
411
}
411
- }
412
}
413
- }
413
+ }
414
`, "json", `upstream: lookup_srv is incompatible with active health checks: 0: {\"dial\": \"\", \"lookup_srv\": \"srv.host.service.consul\"}`)
415
}
416
@@ -440,7 +440,7 @@ func TestReverseProxyHealthCheck(t *testing.T) {
440
health_timeout 100ms
441
}
442
}
443
- `, "caddyfile")
443
+ `, "caddyfile")
444
445
time.Sleep(100 * time.Millisecond) // TODO: for some reason this test seems particularly flaky, getting 503 when it should be 200, unless we wait
446
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
modules/caddyhttp/reverseproxy/httptransport.go
+14
-6
@@ -172,12 +172,19 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
172
}
173
}
174
175
- // Set up the dialer to pull the correct information from the context
175
dialContext := func(ctx context.Context, network, address string) (net.Conn, error) {
177
- // the proper dialing information should be embedded into the request's context
176
+ // For unix socket upstreams, we need to recover the dial info from
177
+ // the request's context, because the Host on the request's URL
178
+ // will have been modified by directing the request, overwriting
179
+ // the unix socket filename.
180
+ // Also, we need to avoid overwriting the address at this point
181
+ // when not necessary, because http.ProxyFromEnvironment may have
182
+ // modified the address according to the user's env proxy config.
183
if dialInfo, ok := GetDialInfo(ctx); ok {
179
- network = dialInfo.Network
180
- address = dialInfo.Address
184
+ if strings.HasPrefix(dialInfo.Network, "unix") {
185
+ network = dialInfo.Network
186
+ address = dialInfo.Address
187
+ }
188
}
189
190
conn, err := dialer.DialContext(ctx, network, address)
@@ -188,8 +195,8 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
195
return nil, DialError{err}
196
}
197
191
- // if read/write timeouts are configured and this is a TCP connection, enforce the timeouts
192
- // by wrapping the connection with our own type
198
+ // if read/write timeouts are configured and this is a TCP connection,
199
+ // enforce the timeouts by wrapping the connection with our own type
200
if tcpConn, ok := conn.(*net.TCPConn); ok && (h.ReadTimeout > 0 || h.WriteTimeout > 0) {
201
conn = &tcpRWTimeoutConn{
202
TCPConn: tcpConn,
@@ -203,6 +210,7 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
210
}
211
212
rt := &http.Transport{
213
+ Proxy: http.ProxyFromEnvironment,
214
DialContext: dialContext,
215
MaxConnsPerHost: h.MaxConnsPerHost,
216
ResponseHeaderTimeout: time.Duration(h.ResponseHeaderTimeout),