From 2bc98abbd9deda03da7ad5e19f865e467b7463cb Mon Sep 17 00:00:00 2001 From: Sonia Appasamy Date: Wed, 9 Aug 2023 13:14:03 -0400 Subject: [PATCH] client/web: add web client Server struct Updates tailscale/corp#13775 Signed-off-by: Sonia Appasamy --- client/web/web.go | 42 ++++++++++++++++++++++++++++------------ cmd/tailscale/cli/web.go | 10 ++++++---- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/client/web/web.go b/client/web/web.go index d10451cbc..6946d9cae 100644 --- a/client/web/web.go +++ b/client/web/web.go @@ -44,7 +44,25 @@ var tmpl *template.Template -var localClient tailscale.LocalClient +// Server is the backend server for a Tailscale web client. +type Server struct { + devMode bool + lc *tailscale.LocalClient +} + +// NewServer constructs a new Tailscale web client server. +// +// lc is an optional parameter. When not filled, NewServer +// initializes its own tailscale.LocalClient. +func NewServer(devMode bool, lc *tailscale.LocalClient) *Server { + if lc == nil { + lc = &tailscale.LocalClient{} + } + return &Server{ + devMode: devMode, + lc: lc, + } +} func init() { tmpl = template.Must(template.New("web.html").Parse(webHTML)) @@ -264,8 +282,8 @@ func synoTokenRedirect(w http.ResponseWriter, r *http.Request) bool { ` -// Handle processes all requests for the Tailscale web client. -func Handle(w http.ResponseWriter, r *http.Request) { +// ServeHTTP processes all requests for the Tailscale web client. +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() if authRedirect(w, r) { return @@ -281,12 +299,12 @@ func Handle(w http.ResponseWriter, r *http.Request) { return } - st, err := localClient.StatusWithoutPeers(ctx) + st, err := s.lc.StatusWithoutPeers(ctx) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - prefs, err := localClient.GetPrefs(ctx) + prefs, err := s.lc.GetPrefs(ctx) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -316,7 +334,7 @@ func Handle(w http.ResponseWriter, r *http.Request) { mp.Prefs.AdvertiseRoutes = routes log.Printf("Doing edit: %v", mp.Pretty()) - if _, err := localClient.EditPrefs(ctx, mp); err != nil { + if _, err := s.lc.EditPrefs(ctx, mp); err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(mi{"error": err.Error()}) return @@ -331,7 +349,7 @@ func Handle(w http.ResponseWriter, r *http.Request) { logout = true } log.Printf("tailscaleUp(reauth=%v, logout=%v) ...", reauth, logout) - url, err := tailscaleUp(r.Context(), st, postData) + url, err := s.tailscaleUp(r.Context(), st, postData) log.Printf("tailscaleUp = (URL %v, %v)", url != "", err) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -386,9 +404,9 @@ func Handle(w http.ResponseWriter, r *http.Request) { w.Write(buf.Bytes()) } -func tailscaleUp(ctx context.Context, st *ipnstate.Status, postData postedData) (authURL string, retErr error) { +func (s *Server) tailscaleUp(ctx context.Context, st *ipnstate.Status, postData postedData) (authURL string, retErr error) { if postData.ForceLogout { - if err := localClient.Logout(ctx); err != nil { + if err := s.lc.Logout(ctx); err != nil { return "", fmt.Errorf("Logout error: %w", err) } return "", nil @@ -415,7 +433,7 @@ func tailscaleUp(ctx context.Context, st *ipnstate.Status, postData postedData) watchCtx, cancelWatch := context.WithCancel(ctx) defer cancelWatch() - watcher, err := localClient.WatchIPNBus(watchCtx, 0) + watcher, err := s.lc.WatchIPNBus(watchCtx, 0) if err != nil { return "", err } @@ -423,10 +441,10 @@ func tailscaleUp(ctx context.Context, st *ipnstate.Status, postData postedData) go func() { if !isRunning { - localClient.Start(ctx, ipn.Options{}) + s.lc.Start(ctx, ipn.Options{}) } if forceReauth { - localClient.StartLoginInteractive(ctx) + s.lc.StartLoginInteractive(ctx) } }() diff --git a/cmd/tailscale/cli/web.go b/cmd/tailscale/cli/web.go index ab591d78c..4862e6fcf 100644 --- a/cmd/tailscale/cli/web.go +++ b/cmd/tailscale/cli/web.go @@ -38,6 +38,7 @@ webf := newFlagSet("web") webf.StringVar(&webArgs.listen, "listen", "localhost:8088", "listen address; use port 0 for automatic") webf.BoolVar(&webArgs.cgi, "cgi", false, "run as CGI script") + webf.BoolVar(&webArgs.dev, "dev", false, "run web client in developer mode") return webf })(), Exec: runWeb, @@ -46,6 +47,7 @@ var webArgs struct { listen string cgi bool + dev bool } func tlsConfigFromEnvironment() *tls.Config { @@ -76,10 +78,10 @@ func runWeb(ctx context.Context, args []string) error { return fmt.Errorf("too many non-flag arguments: %q", args) } - webHandler := http.HandlerFunc(web.Handle) + webServer := web.NewServer(webArgs.dev, nil) if webArgs.cgi { - if err := cgi.Serve(webHandler); err != nil { + if err := cgi.Serve(webServer); err != nil { log.Printf("tailscale.cgi: %v", err) return err } @@ -91,14 +93,14 @@ func runWeb(ctx context.Context, args []string) error { server := &http.Server{ Addr: webArgs.listen, TLSConfig: tlsConfig, - Handler: webHandler, + Handler: webServer, } log.Printf("web server running on: https://%s", server.Addr) return server.ListenAndServeTLS("", "") } else { log.Printf("web server running on: %s", urlOfListenAddr(webArgs.listen)) - return http.ListenAndServe(webArgs.listen, webHandler) + return http.ListenAndServe(webArgs.listen, webServer) } }