Compare commits
2 Commits
b1f9d04b62
...
97e83e5e53
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97e83e5e53 | ||
|
|
ffff0465b0 |
17
go.mod
17
go.mod
@@ -3,28 +3,13 @@ module qgo
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/caddyserver/certmagic v0.25.2
|
||||
github.com/quic-go/quic-go v0.59.0
|
||||
github.com/quic-go/webtransport-go v0.10.0
|
||||
golang.org/x/crypto v0.48.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/caddyserver/zerossl v0.1.5 // 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/quic-go/qpack v0.6.0 // 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
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
)
|
||||
|
||||
46
go.sum
46
go.sum
@@ -1,68 +1,22 @@
|
||||
code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE=
|
||||
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
|
||||
github.com/caddyserver/certmagic v0.25.2 h1:D7xcS7ggX/WEY54x0czj7ioTkmDWKIgxtIi2OcQclUc=
|
||||
github.com/caddyserver/certmagic v0.25.2/go.mod h1:llW/CvsNmza8S6hmsuggsZeiX+uS27dkqY27wDIuBWg=
|
||||
github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE=
|
||||
github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dunglas/httpsfv v1.1.0 h1:Jw76nAyKWKZKFrpMMcL76y35tOpYHqQPzHQiwDvpe54=
|
||||
github.com/dunglas/httpsfv v1.1.0/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
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/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=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ=
|
||||
github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U=
|
||||
github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
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/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=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
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/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=
|
||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
183
main.go
183
main.go
@@ -1,164 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"github.com/quic-go/webtransport-go"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
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 },
|
||||
// 1. Настройка менеджера сертификатов
|
||||
certManager := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist("qgo-server.quizer.space"), // ЗАМЕНИТЕ НА ВАШ ДОМЕН
|
||||
Cache: autocert.DirCache("certs"), // Папка для хранения ключей
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/room/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Сообщаем браузеру о наличии HTTP/3
|
||||
w.Header().Set("Alt-Svc", `h3=":443"; ma=86400`)
|
||||
mux.HandleFunc("/api/hello", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Добавляем CORS для вашего фронтенда
|
||||
w.Header().Set("Access-Control-Allow-Origin", "https://your-svelte-app.com")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
||||
w.Header().Set("Alt-Svc", `h3=":443"; ma=86400`) // Порт 443 стандартный для HTTPS
|
||||
|
||||
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..."))
|
||||
fmt.Printf("Запрос по протоколу: %s\n", r.Proto)
|
||||
w.Write([]byte(`{"message": "Работает через Let's Encrypt и QUIC!"}`))
|
||||
})
|
||||
|
||||
// 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,
|
||||
// Настройка TLS конфигурации для QUIC
|
||||
tlsConfig := &tls.Config{
|
||||
GetCertificate: certManager.GetCertificate,
|
||||
NextProtos: []string{"h3", "h3-29"}, // Поддержка разных версий HTTP/3
|
||||
}
|
||||
|
||||
log.Printf("Starting Server on %s (TCP/UDP 443)", domain)
|
||||
|
||||
// Запускаем TCP сервер в фоне
|
||||
// 2. Запуск HTTP/3 (UDP) на порту 443
|
||||
go func() {
|
||||
if err := tcpServer.ListenAndServeTLS("", ""); err != nil {
|
||||
log.Printf("TCP Server error: %v", err)
|
||||
server := &http3.Server{
|
||||
Addr: ":443",
|
||||
Handler: mux,
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
fmt.Println("Запуск QUIC на :443 UDP...")
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
fmt.Printf("Ошибка QUIC: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Запускаем HTTP/3 сервер (основной поток)
|
||||
if err := h3Server.ListenAndServe(); err != nil {
|
||||
log.Fatalf("H3 Server failed: %v", err)
|
||||
// 3. Запуск стандартного HTTPS (TCP) на порту 443
|
||||
// Это также необходимо для выдачи сертификатов через ACME
|
||||
fmt.Println("Запуск HTTPS на :443 TCP...")
|
||||
httpsServer := &http.Server{
|
||||
Addr: ":443",
|
||||
Handler: mux,
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
|
||||
// Важно: для Let's Encrypt также нужен сервер на порту 80 (HTTP)
|
||||
go http.ListenAndServe(":80", certManager.HTTPHandler(nil))
|
||||
|
||||
if err := httpsServer.ListenAndServeTLS("", ""); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user