loading up the forgejo repo on tangled to test page performance
at forgejo 12 kB view raw
1// Copyright 2014 The Gogs Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package cmd 5 6import ( 7 "context" 8 "fmt" 9 "net" 10 "net/http" 11 "os" 12 "path/filepath" 13 "strconv" 14 "strings" 15 "time" 16 17 _ "net/http/pprof" // Used for debugging if enabled and a web server is running 18 19 "forgejo.org/modules/container" 20 "forgejo.org/modules/graceful" 21 "forgejo.org/modules/log" 22 "forgejo.org/modules/process" 23 "forgejo.org/modules/public" 24 "forgejo.org/modules/setting" 25 "forgejo.org/routers" 26 "forgejo.org/routers/install" 27 28 "github.com/felixge/fgprof" 29 "github.com/urfave/cli/v2" 30) 31 32// PIDFile could be set from build tag 33var PIDFile = "/run/gitea.pid" 34 35// CmdWeb represents the available web sub-command. 36var CmdWeb = &cli.Command{ 37 Name: "web", 38 Usage: "Start the Forgejo web server", 39 Description: `The Forgejo web server is the only thing you need to run, 40and it takes care of all the other things for you`, 41 Before: PrepareConsoleLoggerLevel(log.INFO), 42 Action: runWeb, 43 Flags: []cli.Flag{ 44 &cli.StringFlag{ 45 Name: "port", 46 Aliases: []string{"p"}, 47 Value: "3000", 48 Usage: "Temporary port number to prevent conflict", 49 }, 50 &cli.StringFlag{ 51 Name: "install-port", 52 Value: "3000", 53 Usage: "Temporary port number to run the install page on to prevent conflict", 54 }, 55 &cli.StringFlag{ 56 Name: "pid", 57 Aliases: []string{"P"}, 58 Value: PIDFile, 59 Usage: "Custom pid file path", 60 }, 61 &cli.BoolFlag{ 62 Name: "quiet", 63 Aliases: []string{"q"}, 64 Usage: "Only display Fatal logging errors until logging is set-up", 65 }, 66 &cli.BoolFlag{ 67 Name: "verbose", 68 Usage: "Set initial logging to TRACE level until logging is properly set-up", 69 }, 70 }, 71} 72 73func runHTTPRedirector() { 74 _, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: HTTP Redirector", process.SystemProcessType, true) 75 defer finished() 76 77 source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect) 78 dest := strings.TrimSuffix(setting.AppURL, "/") 79 log.Info("Redirecting: %s to %s", source, dest) 80 81 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 82 target := dest + r.URL.Path 83 if len(r.URL.RawQuery) > 0 { 84 target += "?" + r.URL.RawQuery 85 } 86 http.Redirect(w, r, target, http.StatusTemporaryRedirect) 87 }) 88 89 err := runHTTP("tcp", source, "HTTP Redirector", handler, setting.RedirectorUseProxyProtocol) 90 if err != nil { 91 log.Fatal("Failed to start port redirection: %v", err) 92 } 93} 94 95func createPIDFile(pidPath string) { 96 currentPid := os.Getpid() 97 if err := os.MkdirAll(filepath.Dir(pidPath), os.ModePerm); err != nil { 98 log.Fatal("Failed to create PID folder: %v", err) 99 } 100 101 file, err := os.Create(pidPath) 102 if err != nil { 103 log.Fatal("Failed to create PID file: %v", err) 104 } 105 defer file.Close() 106 if _, err := file.WriteString(strconv.FormatInt(int64(currentPid), 10)); err != nil { 107 log.Fatal("Failed to write PID information: %v", err) 108 } 109} 110 111func showWebStartupMessage(msg string) { 112 log.Info("Forgejo version: %s%s", setting.AppVer, setting.AppBuiltWith) 113 log.Info("* RunMode: %s", setting.RunMode) 114 log.Info("* AppPath: %s", setting.AppPath) 115 log.Info("* WorkPath: %s", setting.AppWorkPath) 116 log.Info("* CustomPath: %s", setting.CustomPath) 117 log.Info("* ConfigFile: %s", setting.CustomConf) 118 log.Info("%s", msg) // show startup message 119 120 if setting.CORSConfig.Enabled { 121 log.Info("CORS Service Enabled") 122 } 123 if setting.DefaultUILocation != time.Local { 124 log.Info("Default UI Location is %v", setting.DefaultUILocation.String()) 125 } 126 if setting.MailService != nil { 127 log.Info("Mail Service Enabled: RegisterEmailConfirm=%v, Service.EnableNotifyMail=%v", setting.Service.RegisterEmailConfirm, setting.Service.EnableNotifyMail) 128 } 129} 130 131func serveInstall(ctx *cli.Context) error { 132 showWebStartupMessage("Prepare to run install page") 133 134 routers.InitWebInstallPage(graceful.GetManager().HammerContext()) 135 136 // Flag for port number in case first time run conflict 137 if ctx.IsSet("port") { 138 if err := setPort(ctx.String("port")); err != nil { 139 return err 140 } 141 } 142 if ctx.IsSet("install-port") { 143 if err := setPort(ctx.String("install-port")); err != nil { 144 return err 145 } 146 } 147 c := install.Routes() 148 err := listen(c, false) 149 if err != nil { 150 log.Critical("Unable to open listener for installer. Is Forgejo already running?") 151 graceful.GetManager().DoGracefulShutdown() 152 } 153 select { 154 case <-graceful.GetManager().IsShutdown(): 155 <-graceful.GetManager().Done() 156 log.Info("PID: %d Forgejo Web Finished", os.Getpid()) 157 log.GetManager().Close() 158 return err 159 default: 160 } 161 return nil 162} 163 164func serveInstalled(ctx *cli.Context) error { 165 setting.InitCfgProvider(setting.CustomConf) 166 setting.LoadCommonSettings() 167 setting.MustInstalled() 168 169 showWebStartupMessage("Prepare to run web server") 170 171 if setting.AppWorkPathMismatch { 172 log.Error("WORK_PATH from config %q doesn't match other paths from environment variables or command arguments. "+ 173 "Only WORK_PATH in config should be set and used. Please make sure the path in config file is correct, "+ 174 "remove the other outdated work paths from environment variables and command arguments", setting.CustomConf) 175 } 176 177 rootCfg := setting.CfgProvider 178 if rootCfg.Section("").Key("WORK_PATH").String() == "" { 179 saveCfg, err := rootCfg.PrepareSaving() 180 if err != nil { 181 log.Error("Unable to prepare saving WORK_PATH=%s to config %q: %v\nYou should set it manually, otherwise there might be bugs when accessing the git repositories.", setting.AppWorkPath, setting.CustomConf, err) 182 } else { 183 rootCfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath) 184 saveCfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath) 185 if err = saveCfg.Save(); err != nil { 186 log.Error("Unable to update WORK_PATH=%s to config %q: %v\nYou should set it manually, otherwise there might be bugs when accessing the git repositories.", setting.AppWorkPath, setting.CustomConf, err) 187 } 188 } 189 } 190 191 // in old versions, user's custom web files are placed in "custom/public", and they were served as "http://domain.com/assets/xxx" 192 // now, Gitea only serves pre-defined files in the "custom/public" folder basing on the web root, the user should move their custom files to "custom/public/assets" 193 publicFiles, _ := public.AssetFS().ListFiles(".") 194 publicFilesSet := container.SetOf(publicFiles...) 195 publicFilesSet.Remove(".well-known") 196 publicFilesSet.Remove("assets") 197 publicFilesSet.Remove("robots.txt") 198 for fn := range publicFilesSet.Seq() { 199 log.Error("Found legacy public asset %q in CustomPath. Please move it to %s/public/assets/%s", fn, setting.CustomPath, fn) 200 } 201 202 routers.InitWebInstalled(graceful.GetManager().HammerContext()) 203 204 // We check that AppDataPath exists here (it should have been created during installation) 205 // We can't check it in `InitWebInstalled`, because some integration tests 206 // use cmd -> InitWebInstalled, but the AppDataPath doesn't exist during those tests. 207 if _, err := os.Stat(setting.AppDataPath); err != nil { 208 log.Fatal("Can not find APP_DATA_PATH %q", setting.AppDataPath) 209 } 210 211 // Override the provided port number within the configuration 212 if ctx.IsSet("port") { 213 if err := setPort(ctx.String("port")); err != nil { 214 return err 215 } 216 } 217 218 // Set up Chi routes 219 webRoutes := routers.NormalRoutes() 220 err := listen(webRoutes, true) 221 <-graceful.GetManager().Done() 222 log.Info("PID: %d Forgejo Web Finished", os.Getpid()) 223 log.GetManager().Close() 224 return err 225} 226 227func servePprof() { 228 http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler()) 229 _, _, finished := process.GetManager().AddTypedContext(context.Background(), "Web: PProf Server", process.SystemProcessType, true) 230 // The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment it's not worth to introduce a configurable option for it. 231 log.Info("Starting pprof server on localhost:6060") 232 log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", nil)) 233 finished() 234} 235 236func runWeb(ctx *cli.Context) error { 237 defer func() { 238 if panicked := recover(); panicked != nil { 239 log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2)) 240 } 241 }() 242 243 managerCtx, cancel := context.WithCancel(context.Background()) 244 graceful.InitManager(managerCtx) 245 defer cancel() 246 247 if os.Getppid() > 1 && len(os.Getenv("LISTEN_FDS")) > 0 { 248 log.Info("Restarting Forgejo on PID: %d from parent PID: %d", os.Getpid(), os.Getppid()) 249 } else { 250 log.Info("Starting Forgejo on PID: %d", os.Getpid()) 251 } 252 253 // Set pid file setting 254 if ctx.IsSet("pid") { 255 createPIDFile(ctx.String("pid")) 256 } 257 258 if !setting.InstallLock { 259 if err := serveInstall(ctx); err != nil { 260 return err 261 } 262 } else { 263 NoInstallListener() 264 } 265 266 if setting.EnablePprof { 267 go servePprof() 268 } 269 270 return serveInstalled(ctx) 271} 272 273func setPort(port string) error { 274 setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1) 275 setting.HTTPPort = port 276 277 switch setting.Protocol { 278 case setting.HTTPUnix: 279 case setting.FCGI: 280 case setting.FCGIUnix: 281 default: 282 defaultLocalURL := string(setting.Protocol) + "://" 283 if setting.HTTPAddr == "0.0.0.0" { 284 defaultLocalURL += "localhost" 285 } else { 286 defaultLocalURL += setting.HTTPAddr 287 } 288 defaultLocalURL += ":" + setting.HTTPPort + "/" 289 290 // Save LOCAL_ROOT_URL if port changed 291 rootCfg := setting.CfgProvider 292 saveCfg, err := rootCfg.PrepareSaving() 293 if err != nil { 294 return fmt.Errorf("failed to save config file: %v", err) 295 } 296 rootCfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL) 297 saveCfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL) 298 if err = saveCfg.Save(); err != nil { 299 return fmt.Errorf("failed to save config file: %v", err) 300 } 301 } 302 return nil 303} 304 305func listen(m http.Handler, handleRedirector bool) error { 306 listenAddr := setting.HTTPAddr 307 if setting.Protocol != setting.HTTPUnix && setting.Protocol != setting.FCGIUnix { 308 listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort) 309 } 310 _, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: Forgejo Server", process.SystemProcessType, true) 311 defer finished() 312 log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL) 313 // This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy. 314 // A user may fix the configuration mistake when he sees this log. 315 // And this is also very helpful to maintainers to provide help to users to resolve their configuration problems. 316 log.Info("AppURL(ROOT_URL): %s", setting.AppURL) 317 318 if setting.LFS.StartServer { 319 log.Info("LFS server enabled") 320 } 321 322 var err error 323 switch setting.Protocol { 324 case setting.HTTP: 325 if handleRedirector { 326 NoHTTPRedirector() 327 } 328 err = runHTTP("tcp", listenAddr, "Web", m, setting.UseProxyProtocol) 329 case setting.HTTPS: 330 if setting.EnableAcme { 331 err = runACME(listenAddr, m) 332 break 333 } 334 if handleRedirector { 335 if setting.RedirectOtherPort { 336 go runHTTPRedirector() 337 } else { 338 NoHTTPRedirector() 339 } 340 } 341 err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging) 342 case setting.FCGI: 343 if handleRedirector { 344 NoHTTPRedirector() 345 } 346 err = runFCGI("tcp", listenAddr, "FCGI Web", m, setting.UseProxyProtocol) 347 case setting.HTTPUnix: 348 if handleRedirector { 349 NoHTTPRedirector() 350 } 351 err = runHTTP("unix", listenAddr, "Web", m, setting.UseProxyProtocol) 352 case setting.FCGIUnix: 353 if handleRedirector { 354 NoHTTPRedirector() 355 } 356 err = runFCGI("unix", listenAddr, "Web", m, setting.UseProxyProtocol) 357 default: 358 log.Fatal("Invalid protocol: %s", setting.Protocol) 359 } 360 if err != nil { 361 log.Critical("Failed to start server: %v", err) 362 } 363 log.Info("HTTP Listener: %s Closed", listenAddr) 364 return err 365}