diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index 3e4eb99..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "crypto/tls" - "log/slog" - "strings" - - "github.com/caddyserver/certmagic" - "github.com/okdaichi/gomoqt/moqt" - - "qgo/internal/relay" -) - -func main() { - domain := "qgo-server.quizer.space" - email := "serverussnap@outlook.com" - - certmagic.DefaultACME.Email = email - certmagic.DefaultACME.Agreed = true - certmagic.Default.Storage = &certmagic.FileStorage{ - Path: "/root/.local/share/certmagic", - } - - tlsConf, err := certmagic.TLS([]string{domain}) - if err != nil { - slog.Error("TLS failed", "err", err) - return - } - - tlsConf.NextProtos = []string{"h3"} - tlsConf.MinVersion = tls.VersionTLS13 - - relayServer := relay.New() - - moqt.HandleFunc("/relay/", func(w moqt.SetupResponseWriter, r *moqt.SetupRequest) { - - room := strings.TrimPrefix(r.Path, "/relay/") - mux := relayServer.GetMux(room) - - _, err := moqt.Accept(w, r, mux) - if err != nil { - slog.Error("accept failed", "err", err) - w.Reject(moqt.InternalSessionErrorCode) - return - } - - slog.Info("session connected", "room", room) - }) - - slog.Info("Mini-SFU running", - "url", "https://"+domain+"/relay/{room}", - ) - - if err := moqt.ListenAndServe(":443", tlsConf); err != nil { - slog.Error("server failed", "err", err) - } -} diff --git a/go.mod b/go.mod index 0e95c35..f65281e 100644 --- a/go.mod +++ b/go.mod @@ -4,23 +4,18 @@ go 1.25.0 require ( github.com/caddyserver/certmagic v0.25.2 - github.com/okdaichi/gomoqt v0.10.7 + github.com/quic-go/quic-go v0.59.0 + github.com/quic-go/webtransport-go v0.10.0 ) require ( github.com/caddyserver/zerossl v0.1.5 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/dunglas/httpsfv v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/libdns/libdns v1.1.1 // indirect github.com/mholt/acmez/v3 v3.1.6 // indirect github.com/miekg/dns v1.1.72 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.59.0 // indirect - github.com/quic-go/webtransport-go v0.10.0 // indirect - github.com/stretchr/objx v0.5.3 // indirect - github.com/stretchr/testify v1.11.1 // indirect github.com/zeebo/blake3 v0.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.1 // indirect @@ -32,5 +27,4 @@ require ( golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect golang.org/x/tools v0.42.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 3d8e28b..b6c16d2 100644 --- a/go.sum +++ b/go.sum @@ -14,10 +14,6 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU= github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk= github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U= @@ -28,8 +24,6 @@ github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk= github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= -github.com/okdaichi/gomoqt v0.10.7 h1:+2fEG8BgBkPPvUorwpaWd9k4IZrtUx+Nr4EDuz8BpYc= -github.com/okdaichi/gomoqt v0.10.7/go.mod h1:a85xgBn+vYbsWTn+cH7YQlnmvSE5m8XptIbP1wEz9v0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= @@ -38,10 +32,6 @@ github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SA github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/quic-go/webtransport-go v0.10.0 h1:LqXXPOXuETY5Xe8ITdGisBzTYmUOy5eSj+9n4hLTjHI= github.com/quic-go/webtransport-go v0.10.0/go.mod h1:LeGIXr5BQKE3UsynwVBeQrU1TPrbh73MGoC6jd+V7ow= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= -github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= @@ -74,8 +64,5 @@ golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/relay/relay.go b/internal/relay/relay.go deleted file mode 100644 index f96232f..0000000 --- a/internal/relay/relay.go +++ /dev/null @@ -1,31 +0,0 @@ -package relay - -import ( - "sync" - - "github.com/okdaichi/gomoqt/moqt" -) - -type Relay struct { - mu sync.RWMutex - rooms map[string]*moqt.TrackMux -} - -func New() *Relay { - return &Relay{ - rooms: make(map[string]*moqt.TrackMux), - } -} - -func (r *Relay) GetMux(room string) *moqt.TrackMux { - r.mu.Lock() - defer r.mu.Unlock() - - if mux, ok := r.rooms[room]; ok { - return mux - } - - mux := moqt.NewTrackMux() - r.rooms[room] = mux - return mux -} diff --git a/main.go b/main.go new file mode 100644 index 0000000..a455138 --- /dev/null +++ b/main.go @@ -0,0 +1,146 @@ +package main + +import ( + "context" + "crypto/tls" + "log" + "net/http" + "strings" + "sync" + + "github.com/caddyserver/certmagic" + "github.com/quic-go/quic-go/http3" + "github.com/quic-go/webtransport-go" +) + +type Room struct { + conns map[*webtransport.Session]bool + mu sync.Mutex +} + +type Server struct { + rooms map[string]*Room + mu sync.Mutex +} + +func NewServer() *Server { + return &Server{ + rooms: make(map[string]*Room), + } +} + +func (s *Server) getRoom(name string) *Room { + s.mu.Lock() + defer s.mu.Unlock() + + if room, ok := s.rooms[name]; ok { + return room + } + + room := &Room{ + conns: make(map[*webtransport.Session]bool), + } + s.rooms[name] = room + return room +} + +func (s *Server) handleSession(roomName string, sess *webtransport.Session) { + room := s.getRoom(roomName) + + room.mu.Lock() + room.conns[sess] = true + room.mu.Unlock() + + log.Println("joined room:", roomName) + + defer func() { + room.mu.Lock() + delete(room.conns, sess) + room.mu.Unlock() + log.Println("left room:", roomName) + }() + + for { + stream, err := sess.AcceptStream(context.Background()) + if err != nil { + return + } + + go s.handleStream(room, sess, stream) + } +} + +func (s *Server) handleStream(room *Room, sender *webtransport.Session, stream *webtransport.Stream) { + buf := make([]byte, 4096) + + for { + n, err := stream.Read(buf) + if err != nil { + return + } + + room.mu.Lock() + for conn := range room.conns { + if conn == sender { + continue + } + go func(c *webtransport.Session) { + out, err := c.OpenStream() + if err != nil { + return + } + defer out.Close() + out.Write(buf[:n]) + }(conn) + } + room.mu.Unlock() + } +} + +func main() { + domain := "qgo-server.quizer.space" + email := "serverussnap@outlook.com" + + certmagic.DefaultACME.Email = email + certmagic.DefaultACME.Agreed = true + certmagic.Default.Storage = &certmagic.FileStorage{ + Path: "/root/.local/share/certmagic", + } + + tlsConf, err := certmagic.TLS([]string{domain}) + if err != nil { + log.Fatal(err) + } + + tlsConf.NextProtos = []string{"h3"} + tlsConf.MinVersion = tls.VersionTLS13 + + server := NewServer() + + mux := http.NewServeMux() + + wtServer := &webtransport.Server{ + H3: &http3.Server{ + Addr: ":443", + TLSConfig: tlsConf, + }, + } + + mux.HandleFunc("/room/", func(w http.ResponseWriter, r *http.Request) { + roomName := strings.TrimPrefix(r.URL.Path, "/room/") + + sess, err := wtServer.Upgrade(w, r) + if err != nil { + log.Println("upgrade error:", err) + return + } + + go server.handleSession(roomName, sess) + }) + + wtServer.H3.Handler = mux + + log.Println("Relay running on https://" + domain + "/room/{room}") + + log.Fatal(wtServer.ListenAndServe()) +} diff --git a/old-test-main.go b/old-test-main.go deleted file mode 100644 index 2f81a2b..0000000 --- a/old-test-main.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "crypto/tls" - "fmt" - "log/slog" - "net/http" - - "github.com/caddyserver/certmagic" - "github.com/okdaichi/gomoqt/moqt" - "github.com/okdaichi/gomoqt/quic" -) - -func main() { - domain := "qgo-server.quizer.space" - email := "serverussnap@outlook.com" - - // 1. Настройка CertMagic - certmagic.DefaultACME.Email = email - certmagic.DefaultACME.Agreed = true - certmagic.Default.Storage = &certmagic.FileStorage{Path: "/root/.local/share/certmagic"} - - // Создаем TLS конфигурацию - tlsConfig, err := certmagic.TLS([]string{domain}) - if err != nil { - slog.Error("failed to setup TLS", "error", err) - return - } - - // Поддержка протоколов для браузера и MoQ - tlsConfig.NextProtos = []string{"h3"} - tlsConfig.MinVersion = tls.VersionTLS13 - - // 2. Инициализация MoQ сервера - moqServer := moqt.Server{ - Addr: ":443", - TLSConfig: tlsConfig, - QUICConfig: &quic.Config{ - Allow0RTT: true, - EnableDatagrams: true, - }, - } - - path := "/relay" - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Alt-Svc", `h3=":443"; ma=2592000`) - - if r.URL.Path == path { - err := moqServer.HandleWebTransport(w, r) - if err != nil { - slog.Error("WebTransport error", "error", err) - } - return - } - fmt.Fprintf(w, "MoQ Server is active at %s", path) - }) - - moqt.HandleFunc(path, func(w moqt.SetupResponseWriter, r *moqt.SetupRequest) { - sess, err := moqt.Accept(w, r, nil) - if err != nil { - return - } - go handleSession(sess) - }) - - slog.Info("Starting MoQ server", "url", "https://"+domain) - - go func() { - err := certmagic.HTTPS([]string{domain}, http.DefaultServeMux) - if err != nil { - slog.Error("TCP/HTTPS Server failed", "error", err) - } - }() - - if err := moqServer.ListenAndServe(); err != nil { - slog.Error("MoQ UDP Server failed", "error", err) - } -} - -func handleSession(sess *moqt.Session) { - slog.Info("MoQ session established", "remote", sess.RemoteAddr()) - <-sess.Context().Done() - slog.Info("MoQ session closed") -}