1
0
Fork 0
httpserver/http.go

218 Zeilen
6.1 KiB
Go

2021-01-09 20:39:05 +00:00
package httpserver
import (
"context"
2021-01-09 20:39:05 +00:00
"crypto/tls"
"html/template"
"net"
2021-01-09 20:39:05 +00:00
"net/http"
"sync"
2021-01-09 20:39:05 +00:00
"github.com/gin-gonic/gin"
"github.com/pelletier/go-toml"
"github.com/phuslu/log"
"go.sebtobie.de/httpserver/auth"
"go.sebtobie.de/httpserver/menus"
"go.sebtobie.de/httpserver/templates"
2021-01-09 20:39:05 +00:00
)
// Config that is used to map the toml config to the settings that are used.
type Config struct {
Addr []string
TLSAddr []string
2021-01-09 20:39:05 +00:00
TLSconfig *tls.Config
Certfile string
Keyfile string
}
// MarshalObject adds the information over the object to the *log.Entry
func (c *Config) MarshalObject(e *log.Entry) {
e.Strs("Address", c.Addr).Bool("TLS", c.TLSconfig != nil)
if c.TLSconfig != nil {
e.Str("Certfile", c.Certfile)
e.Str("Keyfile", c.Keyfile)
}
2021-01-09 20:39:05 +00:00
}
var _ log.ObjectMarshaler = &Config{}
2021-01-09 20:39:05 +00:00
// Server is an wrapper for the *http.Server and *gin.Engine
type Server struct {
2021-01-23 10:14:33 +00:00
http *http.Server
conf *Config
mrouter map[string]*gin.Engine
config *toml.Tree
sites []Site
menu []menus.Menu
template template.Template
NotFoundHandler http.Handler
routines sync.WaitGroup
}
func init() {
gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
log.Trace().Msgf("%-4s(%02d): %-20s %s", httpMethod, nuHandlers, absolutePath, handlerName)
}
2021-01-09 20:39:05 +00:00
}
// runPort runs a listener on the port. his enables th server to serve more than a address.
func (s *Server) runPort(address string, tls bool) {
var socket net.Listener
var err error
if net.ParseIP(address) != nil {
socket, err = net.Listen("tcp", address)
} else {
socket, err = net.Listen("unix", address)
}
if err != nil {
log.Error().Err(err).Msgf("failed to open socket on %s", address)
}
if tls {
err = s.http.ServeTLS(socket, s.conf.Certfile, s.conf.Keyfile)
} else {
err = s.http.Serve(socket)
}
if err != http.ErrServerClosed {
log.Error().Err(err).Msg("Socket unexpected exited")
}
s.routines.Done()
}
2021-01-09 20:39:05 +00:00
// StartServer starts the server as configured and sends the errormessage to the log.
// it blocks until all ports are closed.
2021-01-09 20:39:05 +00:00
func (s *Server) StartServer() {
log.Info().Msg("Starting server")
var err error
if s.conf.Certfile != "" && s.conf.Keyfile != "" {
for _, addr := range s.conf.TLSAddr {
s.routines.Add(1)
go s.runPort(addr, true)
}
}
for _, addr := range s.conf.Addr {
go s.runPort(addr, false)
2021-01-09 20:39:05 +00:00
}
if err != http.ErrServerClosed {
log.Error().Err(err).Msg("Server unexpected exited")
}
s.routines.Wait()
2021-01-09 20:39:05 +00:00
}
2021-01-23 10:14:33 +00:00
// DomainRouter redirects the requests to the routers of the domains
func (s *Server) DomainRouter(w http.ResponseWriter, r *http.Request) {
var domain string
if r.URL.Host != "" {
domain = r.URL.Host
} else if r.Host != "" {
domain = r.Host
} else if r.Header.Get("X-Original-Host") != "" {
domain = r.Header.Get("X-Original-Host")
}
r.Host = domain
r.URL.Host = domain
for header, value := range map[string][]string(r.Header) {
log.Trace().Strs(header, value).Msg("Headers")
}
2021-01-23 10:14:33 +00:00
if router, found := s.mrouter[domain]; found {
router.NoMethod(gin.WrapH(s.NotFoundHandler))
router.NoRoute(gin.WrapH(s.NotFoundHandler))
router.ServeHTTP(w, r)
2021-01-23 10:14:33 +00:00
return
}
var entrys []string
for d := range s.mrouter {
entrys = append(entrys, d)
}
log.Trace().Strs("reqistred domains", entrys).Msg("domain not found")
s.NotFoundHandler.ServeHTTP(w, r)
2021-01-23 10:14:33 +00:00
}
2021-01-09 20:39:05 +00:00
// 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: []string{"127.0.0.1:8080"},
2021-01-09 20:39:05 +00:00
},
2021-01-23 10:14:33 +00:00
mrouter: map[string]*gin.Engine{},
}
2021-01-09 20:39:05 +00:00
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{
ErrorLog: log.DefaultLogger.Std(log.ErrorLevel, log.Context{}, "", 0),
Handler: http.HandlerFunc(server.DomainRouter),
2021-01-09 20:39:05 +00:00
TLSConfig: server.conf.TLSconfig,
}
server.NotFoundHandler = http.NotFoundHandler()
server.menu = []menus.Menu{}
2021-01-09 20:39:05 +00:00
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) {
for _, router := range s.mrouter {
router.Use(m...)
}
2021-01-09 20:39:05 +00:00
}
// Stop Shuts the Server down
func (s *Server) Stop(ctx context.Context) {
2021-01-12 18:29:25 +00:00
log.Info().Err(s.http.Shutdown(ctx)).Msg("Server Shut down.")
for _, site := range s.sites {
site.Teardown()
}
}
2021-01-09 20:39:05 +00:00
// 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()
2021-01-09 20:39:05 +00:00
}
func (s *Server) menus() []menus.Menu {
return s.menu
}
2021-01-09 20:39:05 +00:00
// RegisterSite adds an site to the engine as its own grouo
2021-01-23 10:14:33 +00:00
func (s *Server) RegisterSite(domain, path string, site Site) {
var router *gin.Engine
var found bool
if router, found = s.mrouter[domain]; !found {
var authhf auth.AuthenticationHandler
2021-01-23 10:14:33 +00:00
router = gin.New()
mw := []gin.HandlerFunc{
func(c *gin.Context) {
c.Set(Menus, s.menus)
c.Set(Domain, domain)
},
}
if authhf, found = site.(auth.AuthenticationHandler); !found {
authhf = &auth.AnonAccountHandler{}
}
mw = append(mw, func(c *gin.Context) { authhf.Account(c) })
router.Use(mw...)
2021-01-23 10:14:33 +00:00
s.mrouter[domain] = router
}
site.Init(router.Group(path))
s.sites = append(s.sites, site)
if ms, ok := site.(menus.MenuSite); ok {
2021-01-23 10:14:33 +00:00
menus := ms.Menu(domain)
log.Debug().Msgf("%d menus are added", len(menus))
s.menu = append(s.menu, menus...)
}
if ts, ok := site.(templates.TemplateSite); ok {
templates := ts.Templates()
log.Debug().Msgf("Templates for %s%s are added", domain, path)
s.template.AddParseTree(templates.Name(), templates.Tree)
s.template.Funcs(ts.Funcs())
}
2021-01-09 20:39:05 +00:00
return
}