package awgserver import ( "crypto/tls" "embed" "fmt" "io/fs" "log/slog" "net/http" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/http3" "golang.org/x/crypto/acme/autocert" ) //go:embed static/* var staticFiles embed.FS // startH3Server starts the HTTP/3 server using the FilteredConn from MuxBind. // It handles Let's Encrypt cert issuance and serves embedded static files. func startH3Server(fc *FilteredConn, cfg *Config, logger *slog.Logger) (*http3.Server, error) { certManager := &autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist(cfg.Domain), Cache: autocert.DirCache(cfg.CertCache), } if cfg.ACMEHTTPPort > 0 { go func() { addr := fmt.Sprintf(":%d", cfg.ACMEHTTPPort) logger.Info("ACME HTTP-01 challenge listener started.", "address", addr) if err := http.ListenAndServe(addr, certManager.HTTPHandler(nil)); err != nil { logger.Error("ACME HTTP listener failed.", "err", err) } }() } staticFS, err := fs.Sub(staticFiles, "static") if err != nil { return nil, fmt.Errorf("static files: %w", err) } handler := http.FileServer(http.FS(staticFS)) mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Alt-Svc", fmt.Sprintf(`h3=":%d"; ma=86400`, cfg.ListenPort)) handler.ServeHTTP(w, r) }) transport := &quic.Transport{Conn: fc} h3srv := &http3.Server{ Handler: mux, TLSConfig: &tls.Config{ GetCertificate: certManager.TLSConfig().GetCertificate, NextProtos: []string{"h3"}, }, } ln, err := transport.ListenEarly(h3srv.TLSConfig, &quic.Config{}) if err != nil { return nil, fmt.Errorf("QUIC listen: %w", err) } logger.Info("HTTP/3 server started.", "domain", cfg.Domain, "port", cfg.ListenPort) go func() { if err := h3srv.ServeListener(ln); err != nil { logger.Error("HTTP/3 server failed.", "err", err) } }() return h3srv, nil }