@marcel91200 / caddy Mirror · read-only / commits / 536c28d4
Fork 0

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 +}