ipn/ipnlocal: implement the reverse proxy HTTP handler type

Updates tailscale/corp#7515

Change-Id: Icbfe57f44b9516388edc0556eb04a370a9e3e009
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2022-11-09 21:27:09 -08:00 committed by Brad Fitzpatrick
parent 6beb3184d5
commit 32a4ff3e5f

View File

@ -12,8 +12,11 @@
"io"
"net"
"net/http"
"net/http/httputil"
"net/netip"
"net/url"
pathpkg "path"
"strings"
"time"
"tailscale.com/ipn"
@ -156,13 +159,52 @@ func (b *LocalBackend) serveWebHandler(w http.ResponseWriter, r *http.Request) {
return
}
if v := h.Proxy(); v != "" {
io.WriteString(w, "TODO(bradfitz): proxy")
// TODO(bradfitz): this is a lot of setup per HTTP request. We should
// build the whole http.Handler with all the muxing and child handlers
// only on start/config change. But this works for now (2022-11-09).
u, err := url.Parse(expandProxyArg(v))
if err != nil {
http.Error(w, "bad proxy config", http.StatusInternalServerError)
return
}
rp := httputil.NewSingleHostReverseProxy(u)
rp.Transport = &http.Transport{
DialContext: b.dialer.SystemDial,
}
rp.ServeHTTP(w, r)
return
}
http.Error(w, "empty handler", 500)
}
// expandProxyArg returns a URL from s, where s can be of form:
//
// * port number ("8080")
// * host:port ("localhost:8080")
// * full URL ("http://localhost:8080", in which case it's returned unchanged)
func expandProxyArg(s string) string {
if s == "" {
return ""
}
if strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") {
return s
}
if allNumeric(s) {
return "http://127.0.0.1:" + s
}
return "http://" + s
}
func allNumeric(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] < '0' || s[i] > '9' {
return false
}
}
return s != ""
}
func (b *LocalBackend) webServerConfig(sniName string, port uint16) (c ipn.WebServerConfigView, ok bool) {
key := ipn.HostPort(fmt.Sprintf("%s:%v", sniName, port))