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

100 lines
2.1 KiB
Go

package warp
import (
"context"
"encoding/hex"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"github.com/adriancable/webtransport-go"
"github.com/kixelated/invoker"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/logging"
"github.com/lucas-clemente/quic-go/qlog"
)
type Server struct {
inner *webtransport.Server
media *Media
socket *Socket
sessions invoker.Tasks
}
type ServerConfig struct {
Addr string
CertFile string
KeyFile string
LogDir string
}
func NewServer(config ServerConfig, media *Media) (s *Server, err error) {
s = new(Server)
// Listen using a custom socket that simulates congestion.
s.socket, err = NewSocket(config.Addr)
if err != nil {
return nil, fmt.Errorf("failed to create socket: %w", err)
}
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
})
}
s.inner = &webtransport.Server{
Listen: s.socket,
TLSCert: webtransport.CertFile{Path: config.CertFile},
TLSKey: webtransport.CertFile{Path: config.KeyFile},
QuicConfig: quicConfig,
}
s.media = media
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
session, ok := r.Body.(*webtransport.Session)
if !ok {
log.Print("http requests not supported")
return
}
ss, err := NewSession(session, s.media, s.socket)
if err != nil {
// TODO handle better?
log.Printf("failed to create warp session: %v", err)
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) Run(ctx context.Context) (err error) {
return invoker.Run(ctx, s.inner.Run, s.socket.Run, s.sessions.Repeat)
}