~bigbes/shroud

ref: 999bf1cb8045e0b245640d8f284d92f41b31b8a8 shroud/internal/awgserver/h3server.go -rw-r--r-- 1.9 KiB
999bf1cb — Eugene Blikh feat(reality): add autodetect command for decoy server 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
}