You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

web.go 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package cmd
  5. import (
  6. "context"
  7. "fmt"
  8. "net"
  9. "net/http"
  10. _ "net/http/pprof" // Used for debugging if enabled and a web server is running
  11. "os"
  12. "strings"
  13. "code.gitea.io/gitea/modules/graceful"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/setting"
  16. "code.gitea.io/gitea/routers"
  17. "code.gitea.io/gitea/routers/routes"
  18. "gitea.com/macaron/macaron"
  19. context2 "github.com/gorilla/context"
  20. "github.com/unknwon/com"
  21. "github.com/urfave/cli"
  22. "golang.org/x/crypto/acme/autocert"
  23. ini "gopkg.in/ini.v1"
  24. )
  25. // CmdWeb represents the available web sub-command.
  26. var CmdWeb = cli.Command{
  27. Name: "web",
  28. Usage: "Start Gitea web server",
  29. Description: `Gitea web server is the only thing you need to run,
  30. and it takes care of all the other things for you`,
  31. Action: runWeb,
  32. Flags: []cli.Flag{
  33. cli.StringFlag{
  34. Name: "port, p",
  35. Value: "3000",
  36. Usage: "Temporary port number to prevent conflict",
  37. },
  38. cli.StringFlag{
  39. Name: "install-port",
  40. Value: "3000",
  41. Usage: "Temporary port number to run the install page on to prevent conflict",
  42. },
  43. cli.StringFlag{
  44. Name: "pid, P",
  45. Value: setting.PIDFile,
  46. Usage: "Custom pid file path",
  47. },
  48. },
  49. }
  50. func runHTTPRedirector() {
  51. source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect)
  52. dest := strings.TrimSuffix(setting.AppURL, "/")
  53. log.Info("Redirecting: %s to %s", source, dest)
  54. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  55. target := dest + r.URL.Path
  56. if len(r.URL.RawQuery) > 0 {
  57. target += "?" + r.URL.RawQuery
  58. }
  59. http.Redirect(w, r, target, http.StatusTemporaryRedirect)
  60. })
  61. var err = runHTTP("tcp", source, context2.ClearHandler(handler))
  62. if err != nil {
  63. log.Fatal("Failed to start port redirection: %v", err)
  64. }
  65. }
  66. func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) error {
  67. certManager := autocert.Manager{
  68. Prompt: autocert.AcceptTOS,
  69. HostPolicy: autocert.HostWhitelist(domain),
  70. Cache: autocert.DirCache(directory),
  71. Email: email,
  72. }
  73. go func() {
  74. log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
  75. // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
  76. var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
  77. if err != nil {
  78. log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
  79. }
  80. }()
  81. return runHTTPSWithTLSConfig("tcp", listenAddr, certManager.TLSConfig(), context2.ClearHandler(m))
  82. }
  83. func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
  84. if r.Method != "GET" && r.Method != "HEAD" {
  85. http.Error(w, "Use HTTPS", http.StatusBadRequest)
  86. return
  87. }
  88. // Remove the trailing slash at the end of setting.AppURL, the request
  89. // URI always contains a leading slash, which would result in a double
  90. // slash
  91. target := strings.TrimSuffix(setting.AppURL, "/") + r.URL.RequestURI()
  92. http.Redirect(w, r, target, http.StatusFound)
  93. }
  94. func runWeb(ctx *cli.Context) error {
  95. managerCtx, cancel := context.WithCancel(context.Background())
  96. graceful.InitManager(managerCtx)
  97. defer cancel()
  98. if os.Getppid() > 1 && len(os.Getenv("LISTEN_FDS")) > 0 {
  99. log.Info("Restarting Gitea on PID: %d from parent PID: %d", os.Getpid(), os.Getppid())
  100. } else {
  101. log.Info("Starting Gitea on PID: %d", os.Getpid())
  102. }
  103. // Set pid file setting
  104. if ctx.IsSet("pid") {
  105. setting.PIDFile = ctx.String("pid")
  106. setting.WritePIDFile = true
  107. }
  108. // Perform pre-initialization
  109. needsInstall := routers.PreInstallInit(graceful.GetManager().HammerContext())
  110. if needsInstall {
  111. // Flag for port number in case first time run conflict
  112. if ctx.IsSet("port") {
  113. if err := setPort(ctx.String("port")); err != nil {
  114. return err
  115. }
  116. }
  117. if ctx.IsSet("install-port") {
  118. if err := setPort(ctx.String("install-port")); err != nil {
  119. return err
  120. }
  121. }
  122. m := routes.NewMacaron()
  123. routes.RegisterInstallRoute(m)
  124. err := listen(m, false)
  125. select {
  126. case <-graceful.GetManager().IsShutdown():
  127. <-graceful.GetManager().Done()
  128. log.Info("PID: %d Gitea Web Finished", os.Getpid())
  129. log.Close()
  130. return err
  131. default:
  132. }
  133. } else {
  134. NoInstallListener()
  135. }
  136. if setting.EnablePprof {
  137. go func() {
  138. log.Info("Starting pprof server on localhost:6060")
  139. log.Info("%v", http.ListenAndServe("localhost:6060", nil))
  140. }()
  141. }
  142. log.Info("Global init")
  143. // Perform global initialization
  144. routers.GlobalInit(graceful.GetManager().HammerContext())
  145. // Override the provided port number within the configuration
  146. if ctx.IsSet("port") {
  147. if err := setPort(ctx.String("port")); err != nil {
  148. return err
  149. }
  150. }
  151. // Set up Macaron
  152. m := routes.NewMacaron()
  153. routes.RegisterRoutes(m)
  154. err := listen(m, true)
  155. <-graceful.GetManager().Done()
  156. log.Info("PID: %d Gitea Web Finished", os.Getpid())
  157. log.Close()
  158. return err
  159. }
  160. func setPort(port string) error {
  161. setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1)
  162. setting.HTTPPort = port
  163. switch setting.Protocol {
  164. case setting.UnixSocket:
  165. case setting.FCGI:
  166. case setting.FCGIUnix:
  167. default:
  168. // Save LOCAL_ROOT_URL if port changed
  169. cfg := ini.Empty()
  170. if com.IsFile(setting.CustomConf) {
  171. // Keeps custom settings if there is already something.
  172. if err := cfg.Append(setting.CustomConf); err != nil {
  173. return fmt.Errorf("Failed to load custom conf '%s': %v", setting.CustomConf, err)
  174. }
  175. }
  176. defaultLocalURL := string(setting.Protocol) + "://"
  177. if setting.HTTPAddr == "0.0.0.0" {
  178. defaultLocalURL += "localhost"
  179. } else {
  180. defaultLocalURL += setting.HTTPAddr
  181. }
  182. defaultLocalURL += ":" + setting.HTTPPort + "/"
  183. cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
  184. if err := cfg.SaveTo(setting.CustomConf); err != nil {
  185. return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
  186. }
  187. }
  188. return nil
  189. }
  190. func listen(m *macaron.Macaron, handleRedirector bool) error {
  191. listenAddr := setting.HTTPAddr
  192. if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
  193. listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
  194. }
  195. log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
  196. if setting.LFS.StartServer {
  197. log.Info("LFS server enabled")
  198. }
  199. var err error
  200. switch setting.Protocol {
  201. case setting.HTTP:
  202. if handleRedirector {
  203. NoHTTPRedirector()
  204. }
  205. err = runHTTP("tcp", listenAddr, context2.ClearHandler(m))
  206. case setting.HTTPS:
  207. if setting.EnableLetsEncrypt {
  208. err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
  209. break
  210. }
  211. if handleRedirector {
  212. if setting.RedirectOtherPort {
  213. go runHTTPRedirector()
  214. } else {
  215. NoHTTPRedirector()
  216. }
  217. }
  218. err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
  219. case setting.FCGI:
  220. if handleRedirector {
  221. NoHTTPRedirector()
  222. }
  223. err = runFCGI("tcp", listenAddr, context2.ClearHandler(m))
  224. case setting.UnixSocket:
  225. if handleRedirector {
  226. NoHTTPRedirector()
  227. }
  228. err = runHTTP("unix", listenAddr, context2.ClearHandler(m))
  229. case setting.FCGIUnix:
  230. if handleRedirector {
  231. NoHTTPRedirector()
  232. }
  233. err = runFCGI("unix", listenAddr, context2.ClearHandler(m))
  234. default:
  235. log.Fatal("Invalid protocol: %s", setting.Protocol)
  236. }
  237. if err != nil {
  238. log.Critical("Failed to start server: %v", err)
  239. }
  240. log.Info("HTTP Listener: %s Closed", listenAddr)
  241. return err
  242. }