package httpserver import ( "context" "crypto/tls" "net/http" "github.com/gin-gonic/gin" "github.com/pelletier/go-toml" "github.com/phuslu/log" "go.sebtobie.de/httpserver/auth" "go.sebtobie.de/httpserver/menus" ) // Config that is used to map the toml config to the settings that are used. type Config struct { Addr string TLSconfig *tls.Config Certfile string Keyfile string } // MarshalLogObject adds the information over the object to the *log.Entry func (c *Config) MarshalLogObject(e *log.Entry) { e.Str("Address", c.Addr).Bool("TLS", c.TLSconfig != nil).Strs("Cert", []string{c.Certfile, c.Keyfile}) } // Server is an wrapper for the *http.Server and *gin.Engine type Server struct { http *http.Server conf *Config router *gin.Engine mrouter map[string]*gin.Engine config *toml.Tree authhf auth.AuthenticationHandler sites []Site menus []menus.Menu NotFoundHandler gin.HandlerFunc } // StartServer starts the server as configured and sends the errormessage to the log. func (s *Server) StartServer() { log.Info().Msg("Starting server") var err error if s.conf.Certfile != "" && s.conf.Keyfile != "" { err = s.http.ListenAndServeTLS(s.conf.Certfile, s.conf.Keyfile) } else { err = s.http.ListenAndServe() } if err != http.ErrServerClosed { log.Error().Err(err).Msg("Server unexpected exited") } } // DomainRouter redirects the requests to the routers of the domains func (s *Server) DomainRouter(c *gin.Context) { domain := c.Request.URL.Host entry := log.Trace().Str("domain", domain) if router, found := s.mrouter[domain]; found { entry.Msg("Found domain") router.NoMethod(s.NotFoundHandler) router.NoRoute(s.NotFoundHandler) c.Set("domain", domain) router.HandleContext(c) return } entry.Interface("domains", s.mrouter).Msg("domain not found") s.NotFoundHandler(c) } // CreateServer creates an server that can be run in a coroutine. func CreateServer(config *toml.Tree) *Server { log.Info().Msg("Redirect logging output to phuslu/log") gin.DefaultErrorWriter = log.DefaultLogger.Std(log.ErrorLevel, log.Context{}, "GIN", 0).Writer() gin.DefaultWriter = log.DefaultLogger.Std(log.DebugLevel, log.Context{}, "GIN", 0).Writer() log.Info().Msg("Creating HTTP-Server") var server = &Server{ conf: &Config{ Addr: "127.0.0.1:8080", }, router: gin.New(), mrouter: map[string]*gin.Engine{}, authhf: &auth.AnonAccountHandler{}, } mw := []gin.HandlerFunc{ func(c *gin.Context) { c.Set(Accounts, server.authhf.Account(c)) c.Set(Menus, server.menus) }, server.DomainRouter, } server.Use(mw...) server.router.RouterGroup.Handlers = mw if err := config.Unmarshal(server.conf); err != nil { log.Error().Msg("Problem mapping config to Configstruct") } log.Debug().EmbedObject(server.conf).Msg("Config") server.http = &http.Server{ Addr: server.conf.Addr, ErrorLog: log.DefaultLogger.Std(log.ErrorLevel, log.Context{}, "", 0), Handler: server.router, TLSConfig: server.conf.TLSconfig, } server.NotFoundHandler = gin.WrapH(http.NotFoundHandler()) gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { log.Trace().Msgf("%-4s(%02d): %-20s %s", httpMethod, nuHandlers-len(mw), absolutePath, handlerName) } server.menus = []menus.Menu{} return server } // Use installs the middleware into the router. // The Middleware must be able to detect multiple calls byy itself. Deduplication is not performed. func (s *Server) Use(m ...gin.HandlerFunc) { s.router.Use(m...) } // UseAuthBackend is the funćtion that sets the Handler for the authentication func (s *Server) UseAuthBackend(a auth.AuthenticationHandler) { s.authhf = a } // Stop Shuts the Server down func (s *Server) Stop(ctx context.Context) { log.Info().Err(s.http.Shutdown(ctx)).Msg("Server Shut down.") for _, site := range s.sites { site.Teardown() } } // Site is an Interface to abstract the modularized group of pages. // The Middleware must be able to detect multiple calls byy itself. Deduplication is not performed. type Site interface { Init(*gin.RouterGroup) Teardown() } // RegisterSite adds an site to the engine as its own grouo func (s *Server) RegisterSite(domain, path string, site Site) { var router *gin.Engine var found bool if router, found = s.mrouter[domain]; !found { router = gin.New() s.mrouter[domain] = router } site.Init(router.Group(path)) s.sites = append(s.sites, site) if ms, ok := site.(menus.MenuSite); ok { menus := ms.Menu(domain) log.Debug().Msgf("%d menus are added", len(menus)) s.menus = append(s.menus, menus...) } return }