NewServer constructs a new Tailscale web client server. If err is empty, s is always non-nil. ctx is only required to live the duration of the NewServer call, and not the lifespan of the web server.
(opts ServerOpts)
| 175 | // ctx is only required to live the duration of the NewServer call, |
| 176 | // and not the lifespan of the web server. |
| 177 | func NewServer(opts ServerOpts) (s *Server, err error) { |
| 178 | switch opts.Mode { |
| 179 | case LoginServerMode, ReadOnlyServerMode, ManageServerMode: |
| 180 | // valid types |
| 181 | case "": |
| 182 | return nil, fmt.Errorf("must specify a Mode") |
| 183 | default: |
| 184 | return nil, fmt.Errorf("invalid Mode provided") |
| 185 | } |
| 186 | if opts.LocalClient == nil { |
| 187 | opts.LocalClient = &local.Client{} |
| 188 | } |
| 189 | s = &Server{ |
| 190 | mode: opts.Mode, |
| 191 | polc: cmp.Or(opts.PolicyClient, policyclient.Get()), |
| 192 | logf: opts.Logf, |
| 193 | devMode: envknob.Bool("TS_DEBUG_WEB_CLIENT_DEV"), |
| 194 | lc: opts.LocalClient, |
| 195 | cgiMode: opts.CGIMode, |
| 196 | pathPrefix: opts.PathPrefix, |
| 197 | timeNow: opts.TimeNow, |
| 198 | newAuthURL: opts.NewAuthURL, |
| 199 | waitAuthURL: opts.WaitAuthURL, |
| 200 | originOverride: opts.OriginOverride, |
| 201 | } |
| 202 | if opts.PathPrefix != "" { |
| 203 | // Enforce that path prefix always has a single leading '/' |
| 204 | // so that it is treated as a relative URL path. |
| 205 | // We strip multiple leading '/' to prevent schema-less offsite URLs like "//example.com". |
| 206 | // |
| 207 | // See https://github.com/tailscale/corp/issues/16268. |
| 208 | s.pathPrefix = "/" + strings.TrimLeft(path.Clean(opts.PathPrefix), "/\\") |
| 209 | } |
| 210 | if s.mode == ManageServerMode { |
| 211 | if opts.NewAuthURL == nil { |
| 212 | return nil, fmt.Errorf("must provide a NewAuthURL implementation") |
| 213 | } |
| 214 | if opts.WaitAuthURL == nil { |
| 215 | return nil, fmt.Errorf("must provide WaitAuthURL implementation") |
| 216 | } |
| 217 | } |
| 218 | if s.timeNow == nil { |
| 219 | s.timeNow = time.Now |
| 220 | } |
| 221 | if s.logf == nil { |
| 222 | s.logf = log.Printf |
| 223 | } |
| 224 | s.assetsHandler, s.assetsCleanup = assetsHandler(s.devMode) |
| 225 | |
| 226 | var metric string |
| 227 | s.apiHandler, metric = s.modeAPIHandler(s.mode) |
| 228 | s.apiHandler = s.csrfProtect(s.apiHandler) |
| 229 | |
| 230 | // Don't block startup on reporting metric. |
| 231 | // Report in separate go routine with 5 second timeout. |
| 232 | go func() { |
| 233 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) |
| 234 | defer cancel() |
searching dependent graphs…