2022-06-29 16:17:02 +00:00
|
|
|
package warp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-11-07 09:55:04 +00:00
|
|
|
"crypto/tls"
|
2022-06-29 16:17:02 +00:00
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/kixelated/invoker"
|
|
|
|
"github.com/lucas-clemente/quic-go"
|
2022-11-07 09:55:04 +00:00
|
|
|
"github.com/lucas-clemente/quic-go/http3"
|
2022-06-29 16:17:02 +00:00
|
|
|
"github.com/lucas-clemente/quic-go/logging"
|
|
|
|
"github.com/lucas-clemente/quic-go/qlog"
|
2022-11-07 09:55:04 +00:00
|
|
|
"github.com/marten-seemann/webtransport-go"
|
2022-06-29 16:17:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Server struct {
|
2022-11-07 09:55:04 +00:00
|
|
|
inner *webtransport.Server
|
|
|
|
media *Media
|
2022-06-29 16:17:02 +00:00
|
|
|
|
|
|
|
sessions invoker.Tasks
|
|
|
|
}
|
|
|
|
|
|
|
|
type ServerConfig struct {
|
2022-11-07 09:55:04 +00:00
|
|
|
Addr string
|
|
|
|
Cert *tls.Certificate
|
|
|
|
LogDir string
|
2022-06-29 16:17:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-11-07 09:55:04 +00:00
|
|
|
tlsConfig := &tls.Config{
|
|
|
|
Certificates: []tls.Certificate{*config.Cert},
|
|
|
|
}
|
|
|
|
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
2022-06-29 16:17:02 +00:00
|
|
|
s.inner = &webtransport.Server{
|
2022-11-07 09:55:04 +00:00
|
|
|
H3: http3.Server{
|
|
|
|
TLSConfig: tlsConfig,
|
|
|
|
QuicConfig: quicConfig,
|
|
|
|
Addr: config.Addr,
|
|
|
|
Handler: mux,
|
|
|
|
},
|
|
|
|
CheckOrigin: func(r *http.Request) bool { return true },
|
2022-06-29 16:17:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s.media = media
|
|
|
|
|
2022-11-07 09:55:04 +00:00
|
|
|
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)
|
2022-06-29 16:17:02 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-07 09:55:04 +00:00
|
|
|
ss, err := NewSession(session, s.media)
|
2022-06-29 16:17:02 +00:00
|
|
|
if err != nil {
|
2022-11-07 09:55:04 +00:00
|
|
|
http.Error(w, err.Error(), 500)
|
2022-06-29 16:17:02 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-11-07 09:55:04 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2022-06-29 16:17:02 +00:00
|
|
|
func (s *Server) Run(ctx context.Context) (err error) {
|
2022-11-07 09:55:04 +00:00
|
|
|
return invoker.Run(ctx, s.runServe, s.runShutdown, s.sessions.Repeat)
|
2022-06-29 16:17:02 +00:00
|
|
|
}
|