serve.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. package engine
  2. import (
  3. "errors"
  4. "net/http"
  5. "os"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/caddyserver/certmagic"
  10. log "github.com/sirupsen/logrus"
  11. "github.com/tylerb/graceful"
  12. "github.com/xyproto/env/v2"
  13. "golang.org/x/net/http2"
  14. )
  15. // List of functions to run at shutdown
  16. var (
  17. shutdownFunctions []func()
  18. mut sync.Mutex
  19. completed bool
  20. )
  21. // AtShutdown adds a function to the list of functions that will be ran at shutdown
  22. func AtShutdown(shutdownFunction func()) {
  23. mut.Lock()
  24. defer mut.Unlock()
  25. shutdownFunctions = append(shutdownFunctions, shutdownFunction)
  26. }
  27. // NewGracefulServer creates a new graceful server configuration
  28. func (ac *Config) NewGracefulServer(mux *http.ServeMux, http2support bool, addr string) *graceful.Server {
  29. // Server configuration
  30. s := &http.Server{
  31. Addr: addr,
  32. Handler: mux,
  33. // The timeout values is also the maximum time it can take
  34. // for a complete page of Server-Sent Events (SSE).
  35. ReadTimeout: 10 * time.Second,
  36. WriteTimeout: time.Duration(ac.writeTimeout) * time.Second,
  37. MaxHeaderBytes: 1 << 20,
  38. }
  39. if http2support {
  40. // Enable HTTP/2 support
  41. http2.ConfigureServer(s, nil)
  42. }
  43. gracefulServer := &graceful.Server{
  44. Server: s,
  45. Timeout: ac.shutdownTimeout,
  46. }
  47. // Handle ctrl-c
  48. gracefulServer.ShutdownInitiated = ac.GenerateShutdownFunction(gracefulServer) // for investigating gracefulServer.Interrupted
  49. return gracefulServer
  50. }
  51. // GenerateShutdownFunction generates a function that will run the postponed
  52. // shutdown functions. Note that gracefulServer can be nil. It's only used for
  53. // finding out if the server was interrupted (ctrl-c or killed, SIGINT/SIGTERM)
  54. func (ac *Config) GenerateShutdownFunction(gracefulServer *graceful.Server) func() {
  55. return func() {
  56. mut.Lock()
  57. defer mut.Unlock()
  58. if completed {
  59. // The shutdown functions have already been called
  60. return
  61. }
  62. if ac.verboseMode {
  63. log.Info("Initiating shutdown")
  64. }
  65. // Call the shutdown functions in chronological order (FIFO)
  66. for _, shutdownFunction := range shutdownFunctions {
  67. shutdownFunction()
  68. }
  69. completed = true
  70. if ac.verboseMode {
  71. log.Info("Shutdown complete")
  72. }
  73. // Forced shutdown
  74. if gracefulServer != nil {
  75. if gracefulServer.Interrupted {
  76. // gracefulServer.Stop(forcedShutdownTimeout)
  77. ac.fatalExit(errors.New("Interrupted"))
  78. }
  79. }
  80. // TODO: To implement
  81. //if quicServer != nil {
  82. //fmt.Println("DEBUG: Has QUIC server at shutdown!")
  83. //}
  84. // One final flush
  85. os.Stdout.Sync()
  86. }
  87. }
  88. // Serve HTTP, HTTP/2 and/or HTTPS. Returns an error if unable to serve, or nil when done serving.
  89. func (ac *Config) Serve(mux *http.ServeMux, done, ready chan bool) error {
  90. // If we are not writing internal logs to a file, reduce the verbosity
  91. http2.VerboseLogs = (ac.internalLogFilename != os.DevNull)
  92. if ac.onlyLuaMode {
  93. ready <- true // Send a "ready" message to the REPL
  94. <-done // Wait for a "done" message from the REPL (or just keep waiting)
  95. // Serve nothing
  96. return nil // Done
  97. }
  98. // Channel to wait and see if we should just serve regular HTTP instead
  99. justServeRegularHTTP := make(chan bool)
  100. servingHTTPS := false
  101. servingHTTP := false
  102. // Goroutine that wait for a message to just serve regular HTTP, if needed
  103. go func() {
  104. <-justServeRegularHTTP // Wait for a message to just serve regular HTTP
  105. if strings.HasPrefix(ac.serverAddr, ":") {
  106. log.Info("Serving HTTP on http://localhost" + ac.serverAddr + "/")
  107. } else {
  108. log.Info("Serving HTTP on http://" + ac.serverAddr + "/")
  109. }
  110. mut.Lock()
  111. servingHTTP = true
  112. mut.Unlock()
  113. HTTPserver := ac.NewGracefulServer(mux, false, ac.serverAddr)
  114. // Open the URL before the serving has started, in a short delay
  115. if ac.openURLAfterServing && ac.luaServerFilename != "" {
  116. go func() {
  117. time.Sleep(delayBeforeLaunchingBrowser)
  118. ac.OpenURL(ac.serverHost, ac.serverAddr, false)
  119. }()
  120. }
  121. // Start serving. Shut down gracefully at exit.
  122. if err := HTTPserver.ListenAndServe(); err != nil {
  123. mut.Lock()
  124. servingHTTP = false
  125. mut.Unlock()
  126. // If we can't serve regular HTTP on port 80, give up
  127. ac.fatalExit(err)
  128. }
  129. }()
  130. // Decide which protocol to listen to
  131. switch {
  132. case ac.useCertMagic:
  133. if len(ac.certMagicDomains) == 0 {
  134. log.Warnln("Found no directories looking like domains in the given directory.")
  135. } else if len(ac.certMagicDomains) == 1 {
  136. log.Infof("Serving one domain with CertMagic: %s", ac.certMagicDomains[0])
  137. } else {
  138. log.Infof("Serving %d domains with CertMagic: %s", len(ac.certMagicDomains), strings.Join(ac.certMagicDomains, ", "))
  139. }
  140. mut.Lock()
  141. servingHTTPS = true
  142. mut.Unlock()
  143. // TODO: Look at "Advanced use" at https://github.com/caddyserver/certmagic#examples
  144. // Listen for HTTP and HTTPS requests, for specific domain(s)
  145. go func() {
  146. // If $XDG_CONFIG_DIR is not set, use $HOME.
  147. // If $HOME is not set, use $TMPDIR.
  148. // If $TMPDIR is not set, use /tmp.
  149. certStorageDir := env.StrAlt("XDG_CONFIG_DIR", "HOME", env.Str("TMPDIR", "/tmp"))
  150. defaultEmail := env.Str("LOGNAME", "root") + "@localhost"
  151. if len(ac.certMagicDomains) > 0 {
  152. defaultEmail = "webmaster@" + ac.certMagicDomains[0]
  153. }
  154. certmagic.DefaultACME.Email = env.Str("EMAIL", defaultEmail)
  155. // TODO: Find a way for Algernon users to agree on this manually
  156. certmagic.DefaultACME.Agreed = true
  157. certmagic.Default.Storage = &certmagic.FileStorage{Path: certStorageDir}
  158. if err := certmagic.HTTPS(ac.certMagicDomains, mux); err != nil {
  159. mut.Lock()
  160. servingHTTPS = false
  161. mut.Unlock()
  162. log.Error(err)
  163. // Don't serve HTTP if CertMagic fails, just quit
  164. // justServeRegularHTTP <- true
  165. }
  166. }()
  167. case ac.serveJustQUIC: // Just serve QUIC, but fallback to HTTP
  168. if strings.HasPrefix(ac.serverAddr, ":") {
  169. log.Info("Serving QUIC on https://localhost" + ac.serverAddr + "/")
  170. } else {
  171. log.Info("Serving QUIC on https://" + ac.serverAddr + "/")
  172. }
  173. mut.Lock()
  174. servingHTTPS = true
  175. mut.Unlock()
  176. // Start serving over QUIC
  177. go ac.ListenAndServeQUIC(mux, &mut, justServeRegularHTTP, &servingHTTPS)
  178. case ac.productionMode:
  179. // Listen for both HTTPS+HTTP/2 and HTTP requests, on different ports
  180. if len(ac.serverHost) == 0 {
  181. log.Info("Serving HTTP/2 on https://localhost/")
  182. } else {
  183. log.Info("Serving HTTP/2 on https://" + ac.serverHost + "/")
  184. }
  185. mut.Lock()
  186. servingHTTPS = true
  187. mut.Unlock()
  188. go func() {
  189. // Start serving. Shut down gracefully at exit.
  190. // Listen for HTTPS + HTTP/2 requests
  191. HTTPS2server := ac.NewGracefulServer(mux, true, ac.serverHost+":443")
  192. // Start serving. Shut down gracefully at exit.
  193. if err := HTTPS2server.ListenAndServeTLS(ac.serverCert, ac.serverKey); err != nil {
  194. mut.Lock()
  195. servingHTTPS = false
  196. mut.Unlock()
  197. log.Error(err)
  198. }
  199. }()
  200. if len(ac.serverHost) == 0 {
  201. log.Info("Serving HTTP on http://localhost/")
  202. } else {
  203. log.Info("Serving HTTP on http://" + ac.serverHost + "/")
  204. }
  205. mut.Lock()
  206. servingHTTP = true
  207. mut.Unlock()
  208. go func() {
  209. if ac.redirectHTTP {
  210. // Redirect HTTPS to HTTP
  211. redirectFunc := func(w http.ResponseWriter, req *http.Request) {
  212. http.Redirect(w, req, "https://"+req.Host+req.URL.String(), http.StatusMovedPermanently)
  213. }
  214. if err := http.ListenAndServe(ac.serverHost+":80", http.HandlerFunc(redirectFunc)); err != nil {
  215. mut.Lock()
  216. servingHTTP = false
  217. mut.Unlock()
  218. // If we can't serve regular HTTP on port 80, give up
  219. ac.fatalExit(err)
  220. }
  221. } else {
  222. // Don't redirect, but serve the same contents as the HTTPS server as HTTP on port 80
  223. HTTPserver := ac.NewGracefulServer(mux, false, ac.serverHost+":80")
  224. if err := HTTPserver.ListenAndServe(); err != nil {
  225. mut.Lock()
  226. servingHTTP = false
  227. mut.Unlock()
  228. // If we can't serve regular HTTP on port 80, give up
  229. ac.fatalExit(err)
  230. }
  231. }
  232. }()
  233. case ac.serveJustHTTP2: // It's unusual to serve HTTP/2 without HTTPS
  234. if strings.HasPrefix(ac.serverAddr, ":") {
  235. log.Warn("Serving HTTP/2 without HTTPS (not recommended!) on http://localhost" + ac.serverAddr + "/")
  236. } else {
  237. log.Warn("Serving HTTP/2 without HTTPS (not recommended!) on http://" + ac.serverAddr + "/")
  238. }
  239. mut.Lock()
  240. servingHTTPS = true
  241. mut.Unlock()
  242. go func() {
  243. // Listen for HTTP/2 requests
  244. HTTP2server := ac.NewGracefulServer(mux, true, ac.serverAddr)
  245. // Start serving. Shut down gracefully at exit.
  246. if err := HTTP2server.ListenAndServe(); err != nil {
  247. mut.Lock()
  248. servingHTTPS = false
  249. mut.Unlock()
  250. justServeRegularHTTP <- true
  251. log.Error(err)
  252. }
  253. }()
  254. case !ac.serveJustHTTP2 && !ac.serveJustHTTP:
  255. if strings.HasPrefix(ac.serverAddr, ":") {
  256. log.Info("Serving HTTP/2 on https://localhost" + ac.serverAddr + "/")
  257. } else {
  258. log.Info("Serving HTTP/2 on https://" + ac.serverAddr + "/")
  259. }
  260. mut.Lock()
  261. servingHTTPS = true
  262. mut.Unlock()
  263. // Listen for HTTPS + HTTP/2 requests
  264. HTTPS2server := ac.NewGracefulServer(mux, true, ac.serverAddr)
  265. // Start serving. Shut down gracefully at exit.
  266. go func() {
  267. if err := HTTPS2server.ListenAndServeTLS(ac.serverCert, ac.serverKey); err != nil {
  268. log.Errorf("%s. Not serving HTTP/2.", err)
  269. log.Info("Use the -t flag for serving regular HTTP.")
  270. mut.Lock()
  271. servingHTTPS = false
  272. mut.Unlock()
  273. // If HTTPS failed (perhaps the key + cert are missing),
  274. // serve plain HTTP instead
  275. justServeRegularHTTP <- true
  276. }
  277. }()
  278. default:
  279. mut.Lock()
  280. servingHTTP = true
  281. mut.Unlock()
  282. justServeRegularHTTP <- true
  283. }
  284. // Wait just a tiny bit
  285. time.Sleep(20 * time.Millisecond)
  286. ready <- true // Send a "ready" message to the REPL
  287. // Open the URL, if specified
  288. if ac.openURLAfterServing {
  289. // Open the https:// URL if both http:// and https:// are being served
  290. mut.Lock()
  291. if (!servingHTTP) && (!servingHTTPS) {
  292. ac.fatalExit(errors.New("serving neither over https:// nor over https://"))
  293. }
  294. httpsProtocol := servingHTTPS
  295. mut.Unlock()
  296. ac.OpenURL(ac.serverHost, ac.serverAddr, httpsProtocol)
  297. }
  298. <-done // Wait for a "done" message from the REPL (or just keep waiting)
  299. return nil // Done serving
  300. }