156 Zeilen
4.6 KiB
Go
156 Zeilen
4.6 KiB
Go
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
|
|
}
|