linstrom/web/public/server.go
mstar 9151bfb3be
Some checks are pending
/ docker (push) Waiting to run
Add initial tls support
- Uses certificate and key files provided by admin in config
- Let's Encrypt integration planned, but not even close to working
- Initial HTTP3 Support added
2025-05-27 14:06:08 +02:00

157 lines
4.8 KiB
Go

// Package webpublic contains the public webserver
// which provides the primary and only intended access point
// for interacting with the system.
//
// # Sections
//
// - Frontend: Serves the various web frontend versions
// - Main: The original Linstrom specific frontend
// - NoJs: An entirely serverside rendered frontend, no JS included
// - Custom: Custom frontend files will be served here
//
// - API: Endpoints for the actual interactions
// - Frontend: The API used by the main frontend
// - Masto: Mastodon compatible adapter for internal structures
// - ActivityPub: For integration with the Fediverse via ActivityPub
// - Linstrom-RPC: For Linstrom to Linstrom server communication
//
// # Guarantees
//
// - The Masto and ActivityPub API will remain stable
// - Frontend API might change, but the intended consumer (Main frontend)
// will always be up to date with the changes
// - Linstrom-RPC API is versioned and will keep
// a few versions of backwards compatibility
//
// TODO: Decide how long the Linstrom-RPC API will remain backwards compatible
package webpublic
import (
"context"
"io/fs"
"net/http"
webutils "git.mstar.dev/mstar/goutils/http"
"github.com/quic-go/quic-go/http3"
"github.com/rs/zerolog/log"
"git.mstar.dev/mstar/linstrom/auth-new"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/web/public/api"
webmiddleware "git.mstar.dev/mstar/linstrom/web/public/middleware"
)
type Server struct {
server *http.Server
quicServer *http3.Server
}
func New(addr string, duckImg *string, duckFs fs.FS) *Server {
handler := http.NewServeMux()
handler.Handle("/api/", http.StripPrefix("/api", api.BuildApiRouter()))
handler.HandleFunc("GET /.well-known/webfinger", api.WellKnownWebfinger)
handler.HandleFunc("GET /.well-known/nodeinfo", api.NodeInfoOverview)
handler.HandleFunc("GET /nodeinfo/2.1", api.NodeInfo21)
handler.HandleFunc("GET /nodeinfo/2.0", api.NodeInfo20)
handler.HandleFunc("GET /errors/{name}", errorTypeHandler)
handler.HandleFunc("GET /default-image", buildServeDefaultImage(duckImg, duckFs))
handler.HandleFunc("GET /default-image.webp", buildServeDefaultImage(duckImg, duckFs))
rootHandler := webutils.ChainMiddlewares(
handler,
webutils.BuildLoggingMiddleware(
true,
[]string{"/assets"},
map[string]string{"server": "public"},
),
webmiddleware.AppendFullPathMiddleware,
webmiddleware.TraceRequestInfoMiddleware,
webmiddleware.AddUpgradeHeader,
)
server := http.Server{
Handler: rootHandler,
Addr: addr,
}
quicServer := http3.Server{
Handler: rootHandler,
Addr: addr,
}
if config.GlobalConfig.General.PublicPort != nil {
quicServer.Port = *config.GlobalConfig.General.PublicPort
}
return &Server{server: &server, quicServer: &quicServer}
}
func (s *Server) Start() error {
if config.GlobalConfig.SSL.HandleSSL {
certFile, keyFile, err := auth.TlsFromConfig()
if err != nil {
return err
}
if config.GlobalConfig.Experimental.Http3Support {
errChan := make(chan error, 2)
go func() {
errChan <- s.server.ListenAndServeTLS(certFile, keyFile)
}()
go func() {
errChan <- s.quicServer.ListenAndServeTLS(certFile, keyFile)
}()
err := <-errChan
if err != nil && err != http.ErrServerClosed {
return err
}
// Always gets two values back on the channel since both servers are running
err = <-errChan
if err != nil && err != http.ErrServerClosed {
return err
}
return nil
} else {
return s.server.ListenAndServeTLS(certFile, keyFile)
}
} else {
// But refuse http3 support here, since it doesn't work without tls
return s.server.ListenAndServe()
}
}
func (s *Server) Stop() error {
if config.GlobalConfig.SSL.HandleSSL && config.GlobalConfig.Experimental.Http3Support {
errChan := make(chan error, 2)
go func() {
log.Debug().Msg("Stopping tcp server")
errChan <- s.server.Shutdown(context.Background())
log.Debug().Msg("Tcp server stopped")
}()
go func() {
log.Debug().Msg("Stopping udp server")
errChan <- s.quicServer.Shutdown(context.Background())
log.Debug().Msg("Udp server stopped")
}()
err := <-errChan
if err != nil && err != http.ErrServerClosed {
return err
}
// Always gets two values back on the channel since both servers are running
err = <-errChan
if err != nil && err != http.ErrServerClosed {
return err
}
return nil
} else {
return s.server.Shutdown(context.Background())
}
}
func buildServeDefaultImage(
duckImg *string,
duckFs fs.FS,
) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
http.ServeFileFS(w, r, duckFs, "duck.webp")
// w.Header().Add("Content-Type", "image/webp")
// w.Header().Add("Content-Disposition", "attachment; filename=\"duck.webp\"")
// fmt.Fprint(w, *duckImg)
}
}