MovieNight/main.go
Zorchenhimer 127fb4c3fa Gracefully shutdown HTTP server
Rearranged how the RTMP and HTTP servers are started so they could be
gracefully shutdown easier.

Currently, only the HTTP server is gracefully shutdown.  I'm unsure if
it is even possible for the RTMP server.  The documentation for the RTMP
library is basically non-existent, so it's impossible to know for sure
without diving into the library code.
2021-02-07 15:55:51 -05:00

186 lines
4.7 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"net/http"
"os"
"os/signal"
"time"
"github.com/gorilla/sessions"
"github.com/nareix/joy4/format"
"github.com/nareix/joy4/format/rtmp"
"github.com/zorchenhimer/MovieNight/common"
)
var (
pullEmotes bool
addr string
rtmpAddr string
sKey string
stats = newStreamStats()
sAdminPass string
confFile string
)
func setupSettings() error {
var err error
settings, err = LoadSettings(confFile)
if err != nil {
return fmt.Errorf("Unable to load settings: %s", err)
}
if len(settings.StreamKey) == 0 {
return fmt.Errorf("Missing stream key is settings.json")
}
if sAdminPass != "" {
fmt.Println("Password provided at runtime; ignoring password in set in settings.")
settings.AdminPassword = sAdminPass
}
sstore = sessions.NewCookieStore([]byte(settings.SessionKey))
sstore.Options = &sessions.Options{
Path: "/",
MaxAge: 60 * 60 * 24, // one day
SameSite: http.SameSiteStrictMode,
}
return nil
}
func main() {
flag.StringVar(&addr, "l", "", "host:port of the HTTP server")
flag.StringVar(&rtmpAddr, "r", "", "host:port of the RTMP server")
flag.StringVar(&sKey, "k", "", "Stream key, to protect your stream")
flag.StringVar(&sAdminPass, "a", "", "Set admin password. Overrides configuration in settings.json. This will not write the password to settings.json.")
flag.BoolVar(&pullEmotes, "e", false, "Pull emotes")
flag.StringVar(&confFile, "f", "./settings.json", "URI of the conf file")
flag.Parse()
format.RegisterAll()
if err := setupSettings(); err != nil {
fmt.Printf("Error loading settings: %v\n", err)
os.Exit(1)
}
if pullEmotes {
common.LogInfoln("Pulling emotes")
err := getEmotes(settings.ApprovedEmotes)
if err != nil {
common.LogErrorf("Error downloading emotes: %+v\n", err)
common.LogErrorf("Error downloading emotes: %v\n", err)
os.Exit(1)
}
}
if err := common.InitTemplates(); err != nil {
common.LogErrorln(err)
os.Exit(1)
}
exit := make(chan bool)
go handleInterrupt(exit)
// Load emotes before starting server.
var err error
chat, err = newChatRoom()
if err != nil {
common.LogErrorln(err)
os.Exit(1)
}
if addr == "" {
addr = settings.ListenAddress
}
if rtmpAddr == "" {
rtmpAddr = settings.RtmpListenAddress
}
// A stream key was passed on the command line. Use it, but don't save
// it over the stream key in the settings.json file.
if sKey != "" {
settings.SetTempKey(sKey)
}
common.LogInfoln("Stream key: ", settings.GetStreamKey())
common.LogInfoln("Admin password: ", settings.AdminPassword)
common.LogInfoln("HTTP server listening on: ", addr)
common.LogInfoln("RTMP server listening on: ", rtmpAddr)
common.LogInfoln("RoomAccess: ", settings.RoomAccess)
common.LogInfoln("RoomAccessPin: ", settings.RoomAccessPin)
rtmpServer := &rtmp.Server{
HandlePlay: handlePlay,
HandlePublish: handlePublish,
Addr: rtmpAddr,
}
router := http.NewServeMux()
router.HandleFunc("/ws", wsHandler) // Chat websocket
router.HandleFunc("/static/js/", wsStaticFiles)
router.HandleFunc("/static/css/", wsStaticFiles)
router.HandleFunc("/static/img/", wsImages)
router.HandleFunc("/static/main.wasm", wsWasmFile)
router.HandleFunc("/emotes/", wsEmotes)
router.HandleFunc("/favicon.ico", wsStaticFiles)
router.HandleFunc("/chat", handleIndexTemplate)
router.HandleFunc("/video", handleIndexTemplate)
router.HandleFunc("/help", handleHelpTemplate)
router.HandleFunc("/emotes", handleEmoteTemplate)
router.HandleFunc("/live", handleLive)
router.HandleFunc("/", handleDefault)
httpServer := &http.Server{
Addr: addr,
Handler: router,
}
// RTMP Server
go func() {
err := rtmpServer.ListenAndServe()
if err != nil {
// If the server cannot start, don't pretend we can continue.
panic("Error trying to start rtmp server: " + err.Error())
}
}()
// HTTP Server
go func() {
err := httpServer.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
// If the server cannot start, don't pretend we can continue.
panic("Error trying to start chat/http server: " + err.Error())
}
}()
<-exit
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := httpServer.Shutdown(ctx); err != nil && err != http.ErrServerClosed {
panic("Gracefull HTTP server shutdown failed: " + err.Error())
}
// I don't think the RTMP server can be shutdown cleanly. Apparently the author
// of joy4 want's everyone to use joy5, but that one doesn't seem to allow clean
// shutdowns either? Idk, the documentation on joy4 and joy5 are non-existent.
}
func handleInterrupt(exit chan bool) {
ch := make(chan os.Signal)
signal.Notify(ch, os.Interrupt)
<-ch
common.LogInfoln("Closing server")
if settings.StreamStats {
stats.Print()
}
exit <- true
}