moq-rs/server/internal/warp/server.go

112 lines
2.2 KiB
Go

package warp
import (
"context"
"crypto/tls"
"encoding/hex"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"github.com/kixelated/invoker"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/http3"
"github.com/lucas-clemente/quic-go/logging"
"github.com/lucas-clemente/quic-go/qlog"
"github.com/marten-seemann/webtransport-go"
)
type Server struct {
inner *webtransport.Server
media *Media
sessions invoker.Tasks
}
type ServerConfig struct {
Addr string
Cert *tls.Certificate
LogDir string
}
func NewServer(config ServerConfig, media *Media) (s *Server, err error) {
s = new(Server)
quicConfig := &quic.Config{}
if config.LogDir != "" {
quicConfig.Tracer = qlog.NewTracer(func(p logging.Perspective, connectionID []byte) io.WriteCloser {
path := fmt.Sprintf("%s-%s.qlog", p, hex.EncodeToString(connectionID))
f, err := os.Create(filepath.Join(config.LogDir, path))
if err != nil {
// lame
panic(err)
}
return f
})
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{*config.Cert},
}
mux := http.NewServeMux()
s.inner = &webtransport.Server{
H3: http3.Server{
TLSConfig: tlsConfig,
QuicConfig: quicConfig,
Addr: config.Addr,
Handler: mux,
},
CheckOrigin: func(r *http.Request) bool { return true },
}
s.media = media
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
session, err := s.inner.Upgrade(w, r)
if err != nil {
http.Error(w, "failed to upgrade session", 500)
return
}
ss, err := NewSession(session, s.media)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// Run the session in parallel, logging errors instead of crashing
s.sessions.Add(func(ctx context.Context) (err error) {
err = ss.Run(ctx)
if err != nil {
log.Printf("terminated session: %s", err)
}
return nil
})
})
return s, nil
}
func (s *Server) runServe(ctx context.Context) (err error) {
return s.inner.ListenAndServe()
}
func (s *Server) runShutdown(ctx context.Context) (err error) {
<-ctx.Done()
s.inner.Close()
return ctx.Err()
}
func (s *Server) Run(ctx context.Context) (err error) {
return invoker.Run(ctx, s.runServe, s.runShutdown, s.sessions.Repeat)
}