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
core: Support Windows absolute paths for UDS proxy upstreams (#5114)
* added some tests for parseUpstreamDialAddress Test 4 fails because it produces "[[::1]]:80" instead of "[::1]:80" * support absolute windows path in unix reverse proxy address * make IsUnixNetwork public, support +h2c and reuse it * add new tests
Steffen Brüheim committed
Feb 8, 2023 at 18:05 UTC
536c28d4dc4b048d710bd37903857f0de15ab3c5
4 files changed
+255
-18
listen_unix.go
+2
-2
@@ -34,7 +34,7 @@ import (
34
// reuseUnixSocket copies and reuses the unix domain socket (UDS) if we already
35
// have it open; if not, unlink it so we can have it. No-op if not a unix network.
36
func reuseUnixSocket(network, addr string) (any, error) {
37
- if !isUnixNetwork(network) {
37
+ if !IsUnixNetwork(network) {
38
return nil, nil
39
}
40
@@ -103,7 +103,7 @@ func listenTCPOrUnix(ctx context.Context, lnKey string, network, address string,
103
104
// reusePort sets SO_REUSEPORT. Ineffective for unix sockets.
105
func reusePort(network, address string, conn syscall.RawConn) error {
106
- if isUnixNetwork(network) {
106
+ if IsUnixNetwork(network) {
107
return nil
108
}
109
return conn.Control(func(descriptor uintptr) {
listeners.go
+7
-6
@@ -205,7 +205,7 @@ func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net
205
// IsUnixNetwork returns true if na.Network is
206
// unix, unixgram, or unixpacket.
207
func (na NetworkAddress) IsUnixNetwork() bool {
208
- return isUnixNetwork(na.Network)
208
+ return IsUnixNetwork(na.Network)
209
}
210
211
// JoinHostPort is like net.JoinHostPort, but where the port
@@ -289,8 +289,9 @@ func (na NetworkAddress) String() string {
289
return JoinNetworkAddress(na.Network, na.Host, na.port())
290
}
291
292
-func isUnixNetwork(netw string) bool {
293
- return netw == "unix" || netw == "unixgram" || netw == "unixpacket"
292
+// IsUnixNetwork returns true if the netw is a unix network.
293
+func IsUnixNetwork(netw string) bool {
294
+ return strings.HasPrefix(netw, "unix")
295
}
296
297
// ParseNetworkAddress parses addr into its individual
@@ -310,7 +311,7 @@ func ParseNetworkAddress(addr string) (NetworkAddress, error) {
311
if network == "" {
312
network = "tcp"
313
}
313
- if isUnixNetwork(network) {
314
+ if IsUnixNetwork(network) {
315
return NetworkAddress{
316
Network: network,
317
Host: host,
@@ -353,7 +354,7 @@ func SplitNetworkAddress(a string) (network, host, port string, err error) {
354
network = strings.ToLower(strings.TrimSpace(beforeSlash))
355
a = afterSlash
356
}
356
- if isUnixNetwork(network) {
357
+ if IsUnixNetwork(network) {
358
host = a
359
return
360
}
@@ -384,7 +385,7 @@ func JoinNetworkAddress(network, host, port string) string {
385
if network != "" {
386
a = network + "/"
387
}
387
- if (host != "" && port == "") || isUnixNetwork(network) {
388
+ if (host != "" && port == "") || IsUnixNetwork(network) {
389
a += host
390
} else if port != "" {
391
a += net.JoinHostPort(host, port)
modules/caddyhttp/reverseproxy/addresses.go
+2
-10
@@ -27,9 +27,6 @@ import (
27
// the dial address, including support for a scheme in front
28
// as a shortcut for the port number, and a network type,
29
// for example 'unix' to dial a unix socket.
30
-//
31
-// TODO: the logic in this function is kind of sensitive, we
32
-// need to write tests before making any more changes to it
30
func parseUpstreamDialAddress(upstreamAddr string) (string, string, error) {
31
var network, scheme, host, port string
32
@@ -79,19 +76,14 @@ func parseUpstreamDialAddress(upstreamAddr string) (string, string, error) {
76
77
scheme, host, port = toURL.Scheme, toURL.Hostname(), toURL.Port()
78
} else {
82
- // extract network manually, since caddy.ParseNetworkAddress() will always add one
83
- if beforeSlash, afterSlash, slashFound := strings.Cut(upstreamAddr, "/"); slashFound {
84
- network = strings.ToLower(strings.TrimSpace(beforeSlash))
85
- upstreamAddr = afterSlash
86
- }
79
var err error
88
- host, port, err = net.SplitHostPort(upstreamAddr)
80
+ network, host, port, err = caddy.SplitNetworkAddress(upstreamAddr)
81
if err != nil {
82
host = upstreamAddr
83
}
84
// we can assume a port if only a hostname is specified, but use of a
85
// placeholder without a port likely means a port will be filled in
94
- if port == "" && !strings.Contains(host, "{") {
86
+ if port == "" && !strings.Contains(host, "{") && !caddy.IsUnixNetwork(network) {
87
port = "80"
88
}
89
}
modules/caddyhttp/reverseproxy/addresses_test.go
new
+244
@@ -0,0 +1,244 @@
1
+// Copyright 2015 Matthew Holt and The Caddy Authors
2
+//
3
+// Licensed under the Apache License, Version 2.0 (the "License");
4
+// you may not use this file except in compliance with the License.
5
+// You may obtain a copy of the License at
6
+//
7
+// http://www.apache.org/licenses/LICENSE-2.0
8
+//
9
+// Unless required by applicable law or agreed to in writing, software
10
+// distributed under the License is distributed on an "AS IS" BASIS,
11
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+// See the License for the specific language governing permissions and
13
+// limitations under the License.
14
+
15
+package reverseproxy
16
+
17
+import "testing"
18
+
19
+func TestParseUpstreamDialAddress(t *testing.T) {
20
+ for i, tc := range []struct {
21
+ input string
22
+ expectHostPort string
23
+ expectScheme string
24
+ expectErr bool
25
+ }{
26
+ {
27
+ input: "foo",
28
+ expectHostPort: "foo:80",
29
+ },
30
+ {
31
+ input: "foo:1234",
32
+ expectHostPort: "foo:1234",
33
+ },
34
+ {
35
+ input: "127.0.0.1",
36
+ expectHostPort: "127.0.0.1:80",
37
+ },
38
+ {
39
+ input: "127.0.0.1:1234",
40
+ expectHostPort: "127.0.0.1:1234",
41
+ },
42
+ {
43
+ input: "[::1]",
44
+ expectHostPort: "[::1]:80",
45
+ },
46
+ {
47
+ input: "[::1]:1234",
48
+ expectHostPort: "[::1]:1234",
49
+ },
50
+ {
51
+ input: "{foo}",
52
+ expectHostPort: "{foo}",
53
+ },
54
+ {
55
+ input: "{foo}:80",
56
+ expectHostPort: "{foo}:80",
57
+ },
58
+ {
59
+ input: "{foo}:{bar}",
60
+ expectHostPort: "{foo}:{bar}",
61
+ },
62
+ {
63
+ input: "http://foo",
64
+ expectHostPort: "foo:80",
65
+ expectScheme: "http",
66
+ },
67
+ {
68
+ input: "http://foo:1234",
69
+ expectHostPort: "foo:1234",
70
+ expectScheme: "http",
71
+ },
72
+ {
73
+ input: "http://127.0.0.1",
74
+ expectHostPort: "127.0.0.1:80",
75
+ expectScheme: "http",
76
+ },
77
+ {
78
+ input: "http://127.0.0.1:1234",
79
+ expectHostPort: "127.0.0.1:1234",
80
+ expectScheme: "http",
81
+ },
82
+ {
83
+ input: "http://[::1]",
84
+ expectHostPort: "[::1]:80",
85
+ expectScheme: "http",
86
+ },
87
+ {
88
+ input: "http://[::1]:80",
89
+ expectHostPort: "[::1]:80",
90
+ expectScheme: "http",
91
+ },
92
+ {
93
+ input: "https://foo",
94
+ expectHostPort: "foo:443",
95
+ expectScheme: "https",
96
+ },
97
+ {
98
+ input: "https://foo:1234",
99
+ expectHostPort: "foo:1234",
100
+ expectScheme: "https",
101
+ },
102
+ {
103
+ input: "https://127.0.0.1",
104
+ expectHostPort: "127.0.0.1:443",
105
+ expectScheme: "https",
106
+ },
107
+ {
108
+ input: "https://127.0.0.1:1234",
109
+ expectHostPort: "127.0.0.1:1234",
110
+ expectScheme: "https",
111
+ },
112
+ {
113
+ input: "https://[::1]",
114
+ expectHostPort: "[::1]:443",
115
+ expectScheme: "https",
116
+ },
117
+ {
118
+ input: "https://[::1]:1234",
119
+ expectHostPort: "[::1]:1234",
120
+ expectScheme: "https",
121
+ },
122
+ {
123
+ input: "h2c://foo",
124
+ expectHostPort: "foo:80",
125
+ expectScheme: "h2c",
126
+ },
127
+ {
128
+ input: "h2c://foo:1234",
129
+ expectHostPort: "foo:1234",
130
+ expectScheme: "h2c",
131
+ },
132
+ {
133
+ input: "h2c://127.0.0.1",
134
+ expectHostPort: "127.0.0.1:80",
135
+ expectScheme: "h2c",
136
+ },
137
+ {
138
+ input: "h2c://127.0.0.1:1234",
139
+ expectHostPort: "127.0.0.1:1234",
140
+ expectScheme: "h2c",
141
+ },
142
+ {
143
+ input: "h2c://[::1]",
144
+ expectHostPort: "[::1]:80",
145
+ expectScheme: "h2c",
146
+ },
147
+ {
148
+ input: "h2c://[::1]:1234",
149
+ expectHostPort: "[::1]:1234",
150
+ expectScheme: "h2c",
151
+ },
152
+ {
153
+ input: "unix//var/php.sock",
154
+ expectHostPort: "unix//var/php.sock",
155
+ },
156
+ {
157
+ input: "unix+h2c//var/grpc.sock",
158
+ expectHostPort: "unix//var/grpc.sock",
159
+ expectScheme: "h2c",
160
+ },
161
+ {
162
+ input: "unix/{foo}",
163
+ expectHostPort: "unix/{foo}",
164
+ },
165
+ {
166
+ input: "unix+h2c/{foo}",
167
+ expectHostPort: "unix/{foo}",
168
+ expectScheme: "h2c",
169
+ },
170
+ {
171
+ input: "unix//foo/{foo}/bar",
172
+ expectHostPort: "unix//foo/{foo}/bar",
173
+ },
174
+ {
175
+ input: "unix+h2c//foo/{foo}/bar",
176
+ expectHostPort: "unix//foo/{foo}/bar",
177
+ expectScheme: "h2c",
178
+ },
179
+ {
180
+ input: "http://{foo}",
181
+ expectErr: true,
182
+ },
183
+ {
184
+ input: "http:// :80",
185
+ expectErr: true,
186
+ },
187
+ {
188
+ input: "http://localhost/path",
189
+ expectErr: true,
190
+ },
191
+ {
192
+ input: "http://localhost?key=value",
193
+ expectErr: true,
194
+ },
195
+ {
196
+ input: "http://localhost#fragment",
197
+ expectErr: true,
198
+ },
199
+ {
200
+ input: "http://foo:443",
201
+ expectErr: true,
202
+ },
203
+ {
204
+ input: "https://foo:80",
205
+ expectErr: true,
206
+ },
207
+ {
208
+ input: "h2c://foo:443",
209
+ expectErr: true,
210
+ },
211
+ {
212
+ input: `unix/c:\absolute\path`,
213
+ expectHostPort: `unix/c:\absolute\path`,
214
+ },
215
+ {
216
+ input: `unix+h2c/c:\absolute\path`,
217
+ expectHostPort: `unix/c:\absolute\path`,
218
+ expectScheme: "h2c",
219
+ },
220
+ {
221
+ input: "unix/c:/absolute/path",
222
+ expectHostPort: "unix/c:/absolute/path",
223
+ },
224
+ {
225
+ input: "unix+h2c/c:/absolute/path",
226
+ expectHostPort: "unix/c:/absolute/path",
227
+ expectScheme: "h2c",
228
+ },
229
+ } {
230
+ actualHostPort, actualScheme, err := parseUpstreamDialAddress(tc.input)
231
+ if tc.expectErr && err == nil {
232
+ t.Errorf("Test %d: Expected error but got %v", i, err)
233
+ }
234
+ if !tc.expectErr && err != nil {
235
+ t.Errorf("Test %d: Expected no error but got %v", i, err)
236
+ }
237
+ if actualHostPort != tc.expectHostPort {
238
+ t.Errorf("Test %d: Expected host and port '%s' but got '%s'", i, tc.expectHostPort, actualHostPort)
239
+ }
240
+ if actualScheme != tc.expectScheme {
241
+ t.Errorf("Test %d: Expected scheme '%s' but got '%s'", i, tc.expectScheme, actualScheme)
242
+ }
243
+ }
244
+}