2020-02-11 00:26:04 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2022-11-21 17:36:05 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2023-02-03 14:24:05 +00:00
|
|
|
"github.com/traefik/traefik/v3/pkg/config/static"
|
|
|
|
"github.com/traefik/traefik/v3/pkg/logs"
|
|
|
|
"github.com/traefik/traefik/v3/pkg/udp"
|
2020-02-11 00:26:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// UDPEntryPoints maps UDP entry points by their names.
|
|
|
|
type UDPEntryPoints map[string]*UDPEntryPoint
|
|
|
|
|
|
|
|
// NewUDPEntryPoints returns all the UDP entry points, keyed by name.
|
|
|
|
func NewUDPEntryPoints(cfg static.EntryPoints) (UDPEntryPoints, error) {
|
|
|
|
entryPoints := make(UDPEntryPoints)
|
|
|
|
for entryPointName, entryPoint := range cfg {
|
|
|
|
protocol, err := entryPoint.GetProtocol()
|
|
|
|
if err != nil {
|
2020-05-11 10:06:07 +00:00
|
|
|
return nil, fmt.Errorf("error while building entryPoint %s: %w", entryPointName, err)
|
2020-02-11 00:26:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if protocol != "udp" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
ep, err := NewUDPEntryPoint(entryPoint)
|
|
|
|
if err != nil {
|
2020-05-11 10:06:07 +00:00
|
|
|
return nil, fmt.Errorf("error while building entryPoint %s: %w", entryPointName, err)
|
2020-02-11 00:26:04 +00:00
|
|
|
}
|
|
|
|
entryPoints[entryPointName] = ep
|
|
|
|
}
|
|
|
|
return entryPoints, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start commences the listening for all the entry points.
|
|
|
|
func (eps UDPEntryPoints) Start() {
|
|
|
|
for entryPointName, ep := range eps {
|
2022-11-21 17:36:05 +00:00
|
|
|
ctx := log.With().Str(logs.EntryPointName, entryPointName).Logger().WithContext(context.Background())
|
2020-02-11 00:26:04 +00:00
|
|
|
go ep.Start(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop makes all the entry points stop listening, and release associated resources.
|
|
|
|
func (eps UDPEntryPoints) Stop() {
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
for epn, ep := range eps {
|
|
|
|
wg.Add(1)
|
|
|
|
|
|
|
|
go func(entryPointName string, entryPoint *UDPEntryPoint) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2022-11-21 17:36:05 +00:00
|
|
|
logger := log.With().Str(logs.EntryPointName, entryPointName).Logger()
|
|
|
|
entryPoint.Shutdown(logger.WithContext(context.Background()))
|
2020-02-11 00:26:04 +00:00
|
|
|
|
2022-11-21 17:36:05 +00:00
|
|
|
logger.Debug().Msg("Entry point closed")
|
2020-02-11 00:26:04 +00:00
|
|
|
}(epn, ep)
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switch swaps out all the given handlers in their associated entrypoints.
|
|
|
|
func (eps UDPEntryPoints) Switch(handlers map[string]udp.Handler) {
|
|
|
|
for epName, handler := range handlers {
|
|
|
|
if ep, ok := eps[epName]; ok {
|
|
|
|
ep.Switch(handler)
|
|
|
|
continue
|
|
|
|
}
|
2022-11-21 17:36:05 +00:00
|
|
|
|
|
|
|
log.Error().Str(logs.EntryPointName, epName).Msg("EntryPoint does not exist")
|
2020-02-11 00:26:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// UDPEntryPoint is an entry point where we listen for UDP packets.
|
|
|
|
type UDPEntryPoint struct {
|
|
|
|
listener *udp.Listener
|
|
|
|
switcher *udp.HandlerSwitcher
|
|
|
|
transportConfiguration *static.EntryPointsTransport
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewUDPEntryPoint returns a UDP entry point.
|
|
|
|
func NewUDPEntryPoint(cfg *static.EntryPoint) (*UDPEntryPoint, error) {
|
2024-01-30 13:56:05 +00:00
|
|
|
listenConfig := newListenConfig(cfg)
|
|
|
|
listener, err := udp.Listen(listenConfig, "udp", cfg.GetAddress(), time.Duration(cfg.UDP.Timeout))
|
2020-02-11 00:26:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &UDPEntryPoint{listener: listener, switcher: &udp.HandlerSwitcher{}, transportConfiguration: cfg.Transport}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start commences the listening for ep.
|
|
|
|
func (ep *UDPEntryPoint) Start(ctx context.Context) {
|
2022-11-21 17:36:05 +00:00
|
|
|
log.Ctx(ctx).Debug().Msg("Start UDP Server")
|
2020-02-11 00:26:04 +00:00
|
|
|
for {
|
|
|
|
conn, err := ep.listener.Accept()
|
|
|
|
if err != nil {
|
|
|
|
// Only errClosedListener can happen that's why we return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
go ep.switcher.ServeUDP(conn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown closes ep's listener. It eventually closes all "sessions" and
|
|
|
|
// releases associated resources, but only after it has waited for a graceTimeout,
|
|
|
|
// if any was configured.
|
|
|
|
func (ep *UDPEntryPoint) Shutdown(ctx context.Context) {
|
2022-11-21 17:36:05 +00:00
|
|
|
logger := log.Ctx(ctx)
|
2020-02-11 00:26:04 +00:00
|
|
|
|
|
|
|
reqAcceptGraceTimeOut := time.Duration(ep.transportConfiguration.LifeCycle.RequestAcceptGraceTimeout)
|
|
|
|
if reqAcceptGraceTimeOut > 0 {
|
2022-11-21 17:36:05 +00:00
|
|
|
logger.Info().Msgf("Waiting %s for incoming requests to cease", reqAcceptGraceTimeOut)
|
2020-02-11 00:26:04 +00:00
|
|
|
time.Sleep(reqAcceptGraceTimeOut)
|
|
|
|
}
|
|
|
|
|
|
|
|
graceTimeOut := time.Duration(ep.transportConfiguration.LifeCycle.GraceTimeOut)
|
|
|
|
if err := ep.listener.Shutdown(graceTimeOut); err != nil {
|
2022-11-21 17:36:05 +00:00
|
|
|
logger.Error().Err(err).Send()
|
2020-02-11 00:26:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switch replaces ep's handler with the one given as argument.
|
|
|
|
func (ep *UDPEntryPoint) Switch(handler udp.Handler) {
|
|
|
|
ep.switcher.Switch(handler)
|
|
|
|
}
|