traefik/pkg/server/server_entrypoint_tcp_http3.go
2022-03-17 18:02:08 +01:00

92 lines
2.3 KiB
Go

package server
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
"sync"
"time"
"github.com/lucas-clemente/quic-go/http3"
"github.com/traefik/traefik/v2/pkg/config/static"
"github.com/traefik/traefik/v2/pkg/log"
tcprouter "github.com/traefik/traefik/v2/pkg/server/router/tcp"
)
type http3server struct {
*http3.Server
http3conn net.PacketConn
lock sync.RWMutex
getter func(info *tls.ClientHelloInfo) (*tls.Config, error)
}
func newHTTP3Server(ctx context.Context, configuration *static.EntryPoint, httpsServer *httpServer) (*http3server, error) {
if configuration.HTTP3 == nil {
return nil, nil
}
if configuration.HTTP3.AdvertisedPort < 0 {
return nil, errors.New("advertised port must be greater than or equal to zero")
}
conn, err := net.ListenPacket("udp", configuration.GetAddress())
if err != nil {
return nil, fmt.Errorf("starting listener: %w", err)
}
h3 := &http3server{
http3conn: conn,
getter: func(info *tls.ClientHelloInfo) (*tls.Config, error) {
return nil, errors.New("no tls config")
},
}
h3.Server = &http3.Server{
Port: uint32(configuration.HTTP3.AdvertisedPort),
Server: &http.Server{
Addr: configuration.GetAddress(),
Handler: httpsServer.Server.(*http.Server).Handler,
ErrorLog: httpServerLogger,
ReadTimeout: time.Duration(configuration.Transport.RespondingTimeouts.ReadTimeout),
WriteTimeout: time.Duration(configuration.Transport.RespondingTimeouts.WriteTimeout),
IdleTimeout: time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout),
TLSConfig: &tls.Config{GetConfigForClient: h3.getGetConfigForClient},
},
}
previousHandler := httpsServer.Server.(*http.Server).Handler
httpsServer.Server.(*http.Server).Handler = http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
err := h3.Server.SetQuicHeaders(rw.Header())
if err != nil {
log.FromContext(ctx).Errorf("failed to set HTTP3 headers: %v", err)
}
previousHandler.ServeHTTP(rw, req)
})
return h3, nil
}
func (e *http3server) Start() error {
return e.Serve(e.http3conn)
}
func (e *http3server) Switch(rt *tcprouter.Router) {
e.lock.Lock()
defer e.lock.Unlock()
e.getter = rt.GetTLSGetClientInfo()
}
func (e *http3server) getGetConfigForClient(info *tls.ClientHelloInfo) (*tls.Config, error) {
e.lock.RLock()
defer e.lock.RUnlock()
return e.getter(info)
}