Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
reverse_proxy, php_fastcgi: Fix upstream parsing regression (fix #3101)
Matthew Holt committed
Feb 28, 2020 at 08:57 UTC
c83d40ccd43c8692061732974bd02fb388acd425
1 file changed
+58
-47
modules/caddyhttp/reverseproxy/caddyfile.go
+58
-47
@@ -101,69 +101,80 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
101
// TODO: the logic in this function is kind of sensitive, we need
102
// to write tests before making any more changes to it
103
upstreamDialAddress := func(upstreamAddr string) (string, error) {
104
- // slight hack, to ensure a non-URL parses correctly (simplifies our code paths)
105
- const undefinedScheme = "undefined"
106
- if !strings.Contains(upstreamAddr, "://") {
107
- upstreamAddr = undefinedScheme + "://" + upstreamAddr
108
- }
104
+ var network, scheme, host, port string
105
110
- // convenient way to get desired scheme, host, and port
111
- toURL, err := url.Parse(upstreamAddr)
112
- if err != nil {
113
- return "", d.Errf("parsing upstream address: %v", err)
114
- }
115
- if toURL.Scheme == undefinedScheme {
116
- toURL.Scheme = ""
117
- }
106
+ if strings.Contains(upstreamAddr, "://") {
107
+ toURL, err := url.Parse(upstreamAddr)
108
+ if err != nil {
109
+ return "", d.Errf("parsing upstream URL: %v", err)
110
+ }
111
119
- // there is currently no way to perform a URL rewrite between choosing
120
- // a backend and proxying to it, so we cannot allow extra components
121
- // in backend URLs
122
- if toURL.Path != "" || toURL.RawQuery != "" || toURL.Fragment != "" {
123
- return "", d.Err("for now, URLs for proxy upstreams only support scheme, host, and port components")
124
- }
112
+ // there is currently no way to perform a URL rewrite between choosing
113
+ // a backend and proxying to it, so we cannot allow extra components
114
+ // in backend URLs
115
+ if toURL.Path != "" || toURL.RawQuery != "" || toURL.Fragment != "" {
116
+ return "", d.Err("for now, URLs for proxy upstreams only support scheme, host, and port components")
117
+ }
118
126
- // ensure the port and scheme aren't in conflict
127
- urlPort := toURL.Port()
128
- if toURL.Scheme == "http" && urlPort == "443" {
129
- return "", d.Err("upstream address has conflicting scheme (http://) and port (:443, the HTTPS port)")
130
- }
131
- if toURL.Scheme == "https" && urlPort == "80" {
132
- return "", d.Err("upstream address has conflicting scheme (https://) and port (:80, the HTTP port)")
133
- }
119
+ // ensure the port and scheme aren't in conflict
120
+ urlPort := toURL.Port()
121
+ if toURL.Scheme == "http" && urlPort == "443" {
122
+ return "", d.Err("upstream address has conflicting scheme (http://) and port (:443, the HTTPS port)")
123
+ }
124
+ if toURL.Scheme == "https" && urlPort == "80" {
125
+ return "", d.Err("upstream address has conflicting scheme (https://) and port (:80, the HTTP port)")
126
+ }
127
+
128
+ // if port is missing, attempt to infer from scheme
129
+ if toURL.Port() == "" {
130
+ var toPort string
131
+ switch toURL.Scheme {
132
+ case "", "http":
133
+ toPort = "80"
134
+ case "https":
135
+ toPort = "443"
136
+ }
137
+ toURL.Host = net.JoinHostPort(toURL.Hostname(), toPort)
138
+ }
139
135
- // dial addresses always need a port, so if no port was
136
- // specified, assume the default ports for HTTP(S)
137
- if urlPort == "" {
138
- var toPort string
139
- if toURL.Scheme == "" {
140
- // if no port or scheme is specified, we assume HTTP
141
- toPort = "80"
142
- } else if toURL.Scheme == "https" {
143
- toPort = "443"
140
+ scheme, host, port = toURL.Scheme, toURL.Hostname(), toURL.Port()
141
+ } else {
142
+ // extract network manually, since caddy.ParseNetworkAddress() will always add one
143
+ if idx := strings.Index(upstreamAddr, "/"); idx >= 0 {
144
+ network = strings.ToLower(strings.TrimSpace(upstreamAddr[:idx]))
145
+ upstreamAddr = upstreamAddr[idx+1:]
146
+ }
147
+ var err error
148
+ host, port, err = net.SplitHostPort(upstreamAddr)
149
+ if err != nil {
150
+ host = upstreamAddr
151
}
145
- toURL.Host = net.JoinHostPort(toURL.Host, toPort)
152
}
153
148
- // if port is known and scheme is not, set the scheme
149
- if toURL.Scheme == "" {
150
- if urlPort == "80" {
151
- toURL.Scheme = "http"
152
- } else if urlPort == "443" {
153
- toURL.Scheme = "https"
154
+ // if scheme is not set, we may be able to infer it from a known port
155
+ if scheme == "" {
156
+ if port == "80" {
157
+ scheme = "http"
158
+ } else if port == "443" {
159
+ scheme = "https"
160
}
161
}
162
163
// the underlying JSON does not yet support different
164
// transports (protocols or schemes) to each backend,
165
// so we remember the last one we see and compare them
160
- if commonScheme != "" && toURL.Scheme != commonScheme {
166
+ if commonScheme != "" && scheme != commonScheme {
167
return "", d.Errf("for now, all proxy upstreams must use the same scheme (transport protocol); expecting '%s://' but got '%s://'",
162
- commonScheme, toURL.Scheme)
168
+ commonScheme, scheme)
169
}
164
- commonScheme = toURL.Scheme
170
+ commonScheme = scheme
171
166
- return toURL.Host, nil
172
+ // for simplest possible config, we only need to include
173
+ // the network portion if the user specified one
174
+ if network != "" {
175
+ return caddy.JoinNetworkAddress(network, host, port), nil
176
+ }
177
+ return net.JoinHostPort(host, port), nil
178
}
179
180
for d.Next() {