Files
qgo-server/main.go
Smile Rex b1f9d04b62
All checks were successful
Create and publish a Docker image 🚀 / build-and-push-image (push) Successful in 2m0s
new h3 server
2026-03-08 16:05:03 +03:00

165 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"context"
"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.Printf("Joined room: %s | Total: %d", roomName, len(room.conns))
defer func() {
room.mu.Lock()
delete(room.conns, sess)
room.mu.Unlock()
log.Printf("Left room: %s", 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) {
defer stream.Close()
buf := make([]byte, 4096)
for {
n, err := stream.Read(buf)
if err != nil {
return
}
data := append([]byte(nil), buf[:n]...)
room.mu.Lock()
for conn := range room.conns {
if conn == sender {
continue
}
// Отправляем данные другим участникам
go func(c *webtransport.Session, d []byte) {
out, err := c.OpenStream()
if err != nil {
return
}
defer out.Close()
out.Write(d)
}(conn, data)
}
room.mu.Unlock()
}
}
func main() {
domain := "qgo-server.quizer.space"
email := "serverussnap@outlook.com"
// 1. Настройка CertMagic
certmagic.DefaultACME.Email = email
certmagic.DefaultACME.Agreed = true
cfg, err := certmagic.TLS([]string{domain})
if err != nil {
log.Fatal(err)
}
// Важно для QUIC: устанавливаем NextProtos
cfg.NextProtos = []string{"h3", "h2", "http/1.1"}
server := NewServer()
// 2. Инициализация WebTransport сервера
wt := &webtransport.Server{
CheckOrigin: func(r *http.Request) bool { return true },
}
mux := http.NewServeMux()
mux.HandleFunc("/room/", func(w http.ResponseWriter, r *http.Request) {
// Сообщаем браузеру о наличии HTTP/3
w.Header().Set("Alt-Svc", `h3=":443"; ma=86400`)
if r.Method == http.MethodConnect {
sess, err := wt.Upgrade(w, r)
if err != nil {
log.Printf("Upgrade error: %v", err)
return
}
roomName := strings.TrimPrefix(r.URL.Path, "/room/")
go server.handleSession(roomName, sess)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("Waiting for WebTransport connection..."))
})
// 3. HTTP/3 Сервер (UDP)
h3Server := &http3.Server{
Addr: ":443",
Handler: mux,
TLSConfig: cfg,
EnableDatagrams: true,
}
wt.H3 = h3Server
// 4. Обычный HTTPS Сервер (TCP) — обязателен для первого запроса и Alt-Svc
tcpServer := &http.Server{
Addr: ":443",
Handler: mux,
TLSConfig: cfg,
}
log.Printf("Starting Server on %s (TCP/UDP 443)", domain)
// Запускаем TCP сервер в фоне
go func() {
if err := tcpServer.ListenAndServeTLS("", ""); err != nil {
log.Printf("TCP Server error: %v", err)
}
}()
// Запускаем HTTP/3 сервер (основной поток)
if err := h3Server.ListenAndServe(); err != nil {
log.Fatalf("H3 Server failed: %v", err)
}
}