Add initial tls support
Some checks are pending
/ docker (push) Waiting to run

- 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
This commit is contained in:
Melody Becker 2025-05-27 14:06:08 +02:00
parent 68d7a5e8c3
commit 9151bfb3be
Signed by: mstar
SSH key fingerprint: SHA256:9VAo09aaVNTWKzPW7Hq2LW+ox9OdwmTSHRoD4mlz1yI
6 changed files with 250 additions and 42 deletions

View file

@ -5,8 +5,10 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"database/sql"
"errors"
"os"
"strings"
"time"
@ -40,62 +42,99 @@ func (l *leUser) GetPrivateKey() crypto.PrivateKey {
return l.Key
}
func TlsFromConfig() (*tls.Config, error) {
// Return the path to the certificate file (.crt), the key file (.key) and any errors
func TlsFromConfig() (string, string, error) {
if config.GlobalConfig.SSL.UseLetsEncrypt != nil && *config.GlobalConfig.SSL.UseLetsEncrypt {
return tlsFromLetsEncrypt()
} else {
return tlsFromFile()
}
panic("Not implemented")
}
func tlsFromLetsEncrypt() (*tls.Config, error) {
// Verifies that the relevant config entries are set and are paths to readable files
func tlsFromFile() (string, string, error) {
sslConf := config.GlobalConfig.SSL
if sslConf.CertificateFile == nil {
return "", "", errors.New("need a path to a certificate file to use for tls")
}
if sslConf.CertKeyFile == nil {
return "", "", errors.New("need a path to a certificate key file to use for tls")
}
certInfo, err := os.Stat(*sslConf.CertificateFile)
if err != nil {
return "", "", err
}
if certInfo.IsDir() {
return "", "", errors.New("certificate file is a directory, needs to be a file")
}
keyInfo, err := os.Stat(*sslConf.CertKeyFile)
if err != nil {
return "", "", err
}
if keyInfo.IsDir() {
return "", "", errors.New("key file is a directory, needs to be a file")
}
return *sslConf.CertificateFile, *sslConf.CertKeyFile, nil
}
// Get the LetsEncrypt generated certifiates and store them in temporary files for the server to use
// If the certificate is too old, requests a new one.
func tlsFromLetsEncrypt() (string, string, error) {
metadata, err := dbgen.ServerMetadata.First()
if err != nil {
return nil, err
return "", "", err
}
if !metadata.LELastUpdate.Valid {
return generateLetsEncryptCert(metadata)
if err = generateLetsEncryptCert(metadata); err != nil {
return "", "", err
}
} else if metadata.LELastUpdate.Time.Add(time.Hour * 24 * 60).Before(time.Now()) {
// Let's Encrypt certificates last for 90 days. It is recommended to renew every 60 days
// See https://letsencrypt.org/docs/faq/#what-is-the-lifetime-for-let-s-encrypt-certificates-for-how-long-are-they-valid
return generateLetsEncryptCert(metadata)
if err = generateLetsEncryptCert(metadata); err != nil {
return "", "", err
}
}
panic("Not implemented")
}
// (Re-)Generate a Lets Encrypt certificate using the provided details in the config
// Automatically stores the newly generated certificate in the db, encrypting beforehand
func generateLetsEncryptCert(metadata *models.ServerMetadata) (*tls.Config, error) {
// (Re-)Generate a Lets Encrypt certificate using the provided details in the config.
// Automatically stores the newly generated certificate in the db, encrypting beforehand.
// Also updates the metadata with the new data.
// Does not write the cert to a temporary file.
func generateLetsEncryptCert(metadata *models.ServerMetadata) error {
encKey := []byte(config.GlobalConfig.Storage.EncryptionKey)
// Follow https://go-acme.github.io/lego/usage/library/index.html
var userPrivateKey *ecdsa.PrivateKey
var err error
if len(metadata.LEUserPrivKey) == 0 {
userPrivateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
return err
}
marshalled, err := x509.MarshalECPrivateKey(userPrivateKey)
if err != nil {
return nil, err
return err
}
encryptedKey, err := Encrypt(
[]byte(config.GlobalConfig.Storage.EncryptionKey),
encKey,
marshalled,
)
if err != nil {
return nil, err
return err
}
metadata.LEUserPrivKey = encryptedKey
} else {
decryptedKey, err := Decrypt([]byte(config.GlobalConfig.Storage.EncryptionKey), metadata.LEUserPrivKey)
decryptedKey, err := Decrypt(encKey, metadata.LEUserPrivKey)
if err != nil {
return nil, err
return err
}
userPrivateKey, err = x509.ParseECPrivateKey(decryptedKey)
if err != nil {
return nil, err
return err
}
}
user := leUser{Email: *config.GlobalConfig.SSL.AdminMail}
user := leUser{Email: *config.GlobalConfig.SSL.AdminMail, Key: userPrivateKey}
leConfig := lego.NewConfig(&user)
leConfig.CADirURL = strings.Replace(
config.GlobalConfig.General.GetFullPublicUrl(),
@ -107,22 +146,22 @@ func generateLetsEncryptCert(metadata *models.ServerMetadata) (*tls.Config, erro
leClient, err := lego.NewClient(leConfig)
if err != nil {
return nil, err
return err
}
// TODO: Figure out these servers
err = leClient.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002"))
if err != nil {
return nil, err
return err
}
err = leClient.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001"))
if err != nil {
return nil, err
return err
}
reg, err := leClient.Registration.Register(
registration.RegisterOptions{TermsOfServiceAgreed: true},
)
if err != nil {
return nil, err
return err
}
user.Registration = reg
req := certificate.ObtainRequest{
@ -131,9 +170,35 @@ func generateLetsEncryptCert(metadata *models.ServerMetadata) (*tls.Config, erro
}
certificates, err := leClient.Certificate.Obtain(req)
if err != nil {
return nil, err
return err
}
_ = certificates
// TODO: Do something with certificates here
panic("Not implemented")
metadata.LELastUpdate = sql.NullTime{Valid: true, Time: time.Now()}
metadata.LEDomain = certificates.Domain
metadata.LECertStableUrl = certificates.CertStableURL
metadata.LECertUrl = certificates.CertURL
metadata.LECSR, err = Encrypt(encKey, certificates.CSR)
if err != nil {
return err
}
metadata.LECertificate, err = Encrypt(encKey, certificates.Certificate)
if err != nil {
return err
}
metadata.LECertificate, err = Encrypt(encKey, certificates.Certificate)
if err != nil {
return err
}
metadata.LEPrivateKey, err = Encrypt(encKey, certificates.PrivateKey)
if err != nil {
return err
}
metadata.LEIssuerCertificate, err = Encrypt(encKey, certificates.IssuerCertificate)
if err != nil {
return err
}
err = dbgen.ServerMetadata.Save(metadata)
if err != nil {
return err
}
return nil
}

View file

@ -14,8 +14,10 @@ type ConfigSSL struct {
HandleSSL bool `toml:"handle_ssl"` // Whether Linstrom should handle SSL encryption itself in case of http1/2
// If Linstrom is to handle SSL, whether it should use LetsEncrypt for certificates
UseLetsEncrypt *bool `toml:"use_lets_encrypt"`
// Path to the certificate if Linstrom is to handle SSL while not using LetsEncrypt
// Path to the certificate file (.crt) if Linstrom is to handle SSL while not using LetsEncrypt
CertificateFile *string `toml:"certificate_file"`
// Path to the certificate key file (.key) to handle SSL while not using LetsEncrypt
CertKeyFile *string `toml:"cert_key_file"`
// Mail adress to use in case of using LetsEncrypt
AdminMail *string `toml:"admin_mail"`
}
@ -251,6 +253,14 @@ func (gc *ConfigGeneral) GetFullPublicUrl() string {
return str
}
func (gc *ConfigGeneral) GetFinalPublicPort() int {
if gc.PublicPort != nil {
return *gc.PublicPort
} else {
return gc.PrivatePort
}
}
func (sc *ConfigStorage) BuildPostgresDSN() string {
dsn := fmt.Sprintf(
"host=%s user=%s password=%s dbname=%s port=%d",

6
go.mod
View file

@ -33,11 +33,13 @@ require (
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-sql-driver/mysql v1.9.2 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-webauthn/x v0.1.20 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-tpm v0.9.3 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
@ -52,8 +54,12 @@ require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.64 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.52.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sync v0.13.0 // indirect

18
go.sum
View file

@ -10,8 +10,12 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
@ -27,6 +31,8 @@ github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JS
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE=
github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY=
github.com/go-webauthn/x v0.1.20 h1:brEBDqfiPtNNCdS/peu8gARtq8fIPsHz0VzpPjGvgiw=
@ -44,8 +50,11 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
@ -88,12 +97,18 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/nrednav/cuid2 v1.0.1 h1:aYLDCmGxEij7xCdiV6GVSPSlqFOS6sqHKKvBeKjddVY=
github.com/nrednav/cuid2 v1.0.1/go.mod h1:nH9lUYqbtoVsnpy20etw5q1guTjE99Xy4EpmnK5nKm0=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
@ -114,6 +129,8 @@ github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcY
github.com/yaronf/httpsign v0.3.2 h1:rBYYx9eHm60noI4oC24JAD2tJuM8AUDh9ErJO/FfzBs=
github.com/yaronf/httpsign v0.3.2/go.mod h1:cYB/6toJrJnf4JTLVoo6IHzFH9/Zu1dcKmah4xfX2WA=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
@ -134,6 +151,7 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -0,0 +1,40 @@
package webmiddleware
import (
"fmt"
"net/http"
"git.mstar.dev/mstar/linstrom/config"
)
func AddUpgradeHeader(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.ProtoMajor {
case 1:
// Always offer upgrade to http2
headerText := fmt.Sprintf(
"h2=\":%d\"; ma=3600",
config.GlobalConfig.General.GetFinalPublicPort(),
)
// Offer upgrade to http3 if enabled
if config.GlobalConfig.Experimental.Http3Support && config.GlobalConfig.SSL.HandleSSL {
headerText = fmt.Sprintf(
"h3=\":%d\"; ma=3600, %s",
config.GlobalConfig.General.GetFinalPublicPort(),
headerText,
)
}
w.Header().Add("Alt-Svc", headerText)
case 2:
// Offer upgrade to http3 if enabled
if config.GlobalConfig.Experimental.Http3Support && config.GlobalConfig.SSL.HandleSSL {
headerText := fmt.Sprintf(
"h3=\":%d\"; ma=3600",
config.GlobalConfig.General.GetFinalPublicPort(),
)
w.Header().Add("Alt-Svc", headerText)
}
}
h.ServeHTTP(w, r)
})
}

View file

@ -32,13 +32,18 @@ import (
"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
server *http.Server
quicServer *http3.Server
}
func New(addr string, duckImg *string, duckFs fs.FS) *Server {
@ -51,28 +56,92 @@ func New(addr string, duckImg *string, duckFs fs.FS) *Server {
handler.HandleFunc("GET /errors/{name}", errorTypeHandler)
handler.HandleFunc("GET /default-image", buildServeDefaultImage(duckImg, duckFs))
handler.HandleFunc("GET /default-image.webp", buildServeDefaultImage(duckImg, duckFs))
server := http.Server{
Handler: webutils.ChainMiddlewares(
handler,
webutils.BuildLoggingMiddleware(
true,
[]string{"/assets"},
map[string]string{"server": "public"},
),
webmiddleware.AppendFullPathMiddleware,
webmiddleware.TraceRequestInfoMiddleware,
rootHandler := webutils.ChainMiddlewares(
handler,
webutils.BuildLoggingMiddleware(
true,
[]string{"/assets"},
map[string]string{"server": "public"},
),
Addr: addr,
webmiddleware.AppendFullPathMiddleware,
webmiddleware.TraceRequestInfoMiddleware,
webmiddleware.AddUpgradeHeader,
)
server := http.Server{
Handler: rootHandler,
Addr: addr,
}
return &Server{&server}
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 {
return s.server.ListenAndServe()
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 {
return s.server.Shutdown(context.Background())
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(