package main import ( "context" "fmt" "log" "net/http" "net/http/httputil" "net/url" "os" "os/signal" "strings" "tailscale.com/tsnet" ) func must[T any](t T, err error) T { if err != nil { panic(err) } return t } func main() { var servers []*http.Server upstreamsConfig := os.Getenv("UPSTREAMS") hosts := strings.Split(upstreamsConfig, ",") if upstreamsConfig == "" || len(hosts) == 0 { log.Fatalf("set UPSTREAMS in environment like 'dockerhost:7400,otherdocker:80'") } for _, upstream := range hosts { hostname, port, ok := strings.Cut(upstream, ":") if hostname == "" { // parsing error, just skip it continue } if !ok || port == "" { // default to 80 port = "80" } s := &tsnet.Server{ Dir: "/.config/" + hostname, Hostname: hostname, Logf: func(s string, a ...any) { // silence most tsnet logs if strings.HasPrefix(s, "To start this tsnet server") { log.Printf(s, a...) } }, } defer s.Close() ln, err := s.Listen("tcp", ":80") if err != nil { fmt.Printf("%+v\n", err) return } defer ln.Close() srv := &http.Server{ Handler: httputil.NewSingleHostReverseProxy( must(url.Parse("http://" + hostname + ":" + port + "/")), ), } servers = append(servers, srv) // start server go func() { err = srv.Serve(ln) fmt.Printf("%+v\n", err) }() } sigint := make(chan os.Signal, 1) signal.Notify(sigint, os.Interrupt) <-sigint for _, server := range servers { if err := server.Shutdown(context.Background()); err != nil { log.Printf("HTTP server Shutdown: %v", err) } } }