2019-03-14 09:30:04 +01:00
package server
import (
"context"
2020-10-28 15:32:04 +01:00
"errors"
2024-01-02 16:40:06 +01:00
"expvar"
2019-03-14 09:30:04 +01:00
"fmt"
2019-09-09 14:52:04 +02:00
stdlog "log"
2019-03-14 09:30:04 +01:00
"net"
"net/http"
2022-08-09 17:36:08 +02:00
"net/url"
2022-04-04 11:46:07 +02:00
"os"
"strings"
2019-03-14 09:30:04 +01:00
"sync"
2020-10-28 15:32:04 +01:00
"syscall"
2019-03-14 09:30:04 +01:00
"time"
2022-02-14 17:18:08 +01:00
"github.com/containous/alice"
2020-11-17 13:04:04 +01:00
"github.com/pires/go-proxyproto"
2019-09-09 14:52:04 +02:00
"github.com/sirupsen/logrus"
2020-09-16 15:46:04 +02:00
"github.com/traefik/traefik/v2/pkg/config/static"
"github.com/traefik/traefik/v2/pkg/ip"
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/middlewares"
"github.com/traefik/traefik/v2/pkg/middlewares/forwardedheaders"
2022-02-14 17:18:08 +01:00
"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
2020-09-16 15:46:04 +02:00
"github.com/traefik/traefik/v2/pkg/safe"
"github.com/traefik/traefik/v2/pkg/server/router"
2022-03-17 11:02:08 -06:00
tcprouter "github.com/traefik/traefik/v2/pkg/server/router/tcp"
2024-02-06 17:34:07 +01:00
"github.com/traefik/traefik/v2/pkg/server/service"
2020-09-16 15:46:04 +02:00
"github.com/traefik/traefik/v2/pkg/tcp"
2022-02-14 17:18:08 +01:00
"github.com/traefik/traefik/v2/pkg/types"
2019-07-01 15:08:04 +02:00
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
2019-03-14 09:30:04 +01:00
)
2019-09-09 14:52:04 +02:00
var httpServerLogger = stdlog . New ( log . WithoutContext ( ) . WriterLevel ( logrus . DebugLevel ) , "" , 0 )
2024-01-02 16:40:06 +01:00
type key string
const (
connStateKey key = "connState"
debugConnectionEnv string = "DEBUG_CONNECTION"
)
var (
clientConnectionStates = map [ string ] * connState { }
clientConnectionStatesMu = sync . RWMutex { }
)
type connState struct {
State string
KeepAliveState string
Start time . Time
HTTPRequestCount int
}
2019-03-14 09:30:04 +01:00
type httpForwarder struct {
net . Listener
connChan chan net . Conn
2020-08-17 12:02:03 +02:00
errChan chan error
2019-03-14 09:30:04 +01:00
}
func newHTTPForwarder ( ln net . Listener ) * httpForwarder {
return & httpForwarder {
Listener : ln ,
connChan : make ( chan net . Conn ) ,
2020-08-17 12:02:03 +02:00
errChan : make ( chan error ) ,
2019-03-14 09:30:04 +01:00
}
}
2020-05-11 12:06:07 +02:00
// ServeTCP uses the connection to serve it later in "Accept".
2019-09-13 17:46:04 +02:00
func ( h * httpForwarder ) ServeTCP ( conn tcp . WriteCloser ) {
2019-03-14 09:30:04 +01:00
h . connChan <- conn
}
2020-05-11 12:06:07 +02:00
// Accept retrieves a served connection in ServeTCP.
2019-03-14 09:30:04 +01:00
func ( h * httpForwarder ) Accept ( ) ( net . Conn , error ) {
2020-08-17 12:02:03 +02:00
select {
case conn := <- h . connChan :
return conn , nil
case err := <- h . errChan :
return nil , err
}
2019-03-14 09:30:04 +01:00
}
2020-05-11 12:06:07 +02:00
// TCPEntryPoints holds a map of TCPEntryPoint (the entrypoint names being the keys).
2019-03-14 09:30:04 +01:00
type TCPEntryPoints map [ string ] * TCPEntryPoint
2019-11-14 16:40:05 +01:00
// NewTCPEntryPoints creates a new TCPEntryPoints.
2022-02-14 17:18:08 +01:00
func NewTCPEntryPoints ( entryPointsConfig static . EntryPoints , hostResolverConfig * types . HostResolverConfig ) ( TCPEntryPoints , error ) {
2024-01-02 16:40:06 +01:00
if os . Getenv ( debugConnectionEnv ) != "" {
expvar . Publish ( "clientConnectionStates" , expvar . Func ( func ( ) any {
return clientConnectionStates
} ) )
}
2019-11-14 16:40:05 +01:00
serverEntryPointsTCP := make ( TCPEntryPoints )
2020-01-06 16:56:05 +01:00
for entryPointName , config := range entryPointsConfig {
2020-02-11 01:26:04 +01:00
protocol , err := config . GetProtocol ( )
if err != nil {
2020-05-11 12:06:07 +02:00
return nil , fmt . Errorf ( "error while building entryPoint %s: %w" , entryPointName , err )
2020-02-11 01:26:04 +01:00
}
if protocol != "tcp" {
continue
}
2019-11-14 16:40:05 +01:00
ctx := log . With ( context . Background ( ) , log . Str ( log . EntryPointName , entryPointName ) )
2022-02-14 17:18:08 +01:00
serverEntryPointsTCP [ entryPointName ] , err = NewTCPEntryPoint ( ctx , config , hostResolverConfig )
2019-11-14 16:40:05 +01:00
if err != nil {
2020-05-11 12:06:07 +02:00
return nil , fmt . Errorf ( "error while building entryPoint %s: %w" , entryPointName , err )
2019-11-14 16:40:05 +01:00
}
}
return serverEntryPointsTCP , nil
}
// Start the server entry points.
func ( eps TCPEntryPoints ) Start ( ) {
for entryPointName , serverEntryPoint := range eps {
ctx := log . With ( context . Background ( ) , log . Str ( log . EntryPointName , entryPointName ) )
2020-02-11 01:26:04 +01:00
go serverEntryPoint . Start ( ctx )
2019-11-14 16:40:05 +01:00
}
}
// Stop the server entry points.
func ( eps TCPEntryPoints ) Stop ( ) {
var wg sync . WaitGroup
for epn , ep := range eps {
wg . Add ( 1 )
go func ( entryPointName string , entryPoint * TCPEntryPoint ) {
defer wg . Done ( )
ctx := log . With ( context . Background ( ) , log . Str ( log . EntryPointName , entryPointName ) )
entryPoint . Shutdown ( ctx )
log . FromContext ( ctx ) . Debugf ( "Entry point %s closed" , entryPointName )
} ( epn , ep )
}
wg . Wait ( )
}
// Switch the TCP routers.
2022-03-17 11:02:08 -06:00
func ( eps TCPEntryPoints ) Switch ( routersTCP map [ string ] * tcprouter . Router ) {
2019-11-14 16:40:05 +01:00
for entryPointName , rt := range routersTCP {
eps [ entryPointName ] . SwitchRouter ( rt )
}
}
2020-05-11 12:06:07 +02:00
// TCPEntryPoint is the TCP server.
2019-03-14 09:30:04 +01:00
type TCPEntryPoint struct {
listener net . Listener
switcher * tcp . HandlerSwitcher
transportConfiguration * static . EntryPointsTransport
tracker * connectionTracker
httpServer * httpServer
httpsServer * httpServer
2021-01-07 14:48:04 +01:00
http3Server * http3server
2019-03-14 09:30:04 +01:00
}
2020-05-11 12:06:07 +02:00
// NewTCPEntryPoint creates a new TCPEntryPoint.
2022-02-14 17:18:08 +01:00
func NewTCPEntryPoint ( ctx context . Context , configuration * static . EntryPoint , hostResolverConfig * types . HostResolverConfig ) ( * TCPEntryPoint , error ) {
2019-03-14 09:30:04 +01:00
tracker := newConnectionTracker ( )
listener , err := buildListener ( ctx , configuration )
if err != nil {
2020-05-11 12:06:07 +02:00
return nil , fmt . Errorf ( "error preparing server: %w" , err )
2019-03-14 09:30:04 +01:00
}
2024-09-13 15:54:04 +02:00
rt , err := tcprouter . NewRouter ( )
if err != nil {
return nil , fmt . Errorf ( "error preparing tcp router: %w" , err )
}
2019-03-14 09:30:04 +01:00
2022-02-14 17:18:08 +01:00
reqDecorator := requestdecorator . New ( hostResolverConfig )
httpServer , err := createHTTPServer ( ctx , listener , configuration , true , reqDecorator )
2019-03-14 09:30:04 +01:00
if err != nil {
2022-02-15 16:04:09 +01:00
return nil , fmt . Errorf ( "error preparing http server: %w" , err )
2019-03-14 09:30:04 +01:00
}
2022-03-17 11:02:08 -06:00
rt . SetHTTPForwarder ( httpServer . Forwarder )
2019-03-14 09:30:04 +01:00
2022-02-14 17:18:08 +01:00
httpsServer , err := createHTTPServer ( ctx , listener , configuration , false , reqDecorator )
2019-03-14 09:30:04 +01:00
if err != nil {
2022-02-15 16:04:09 +01:00
return nil , fmt . Errorf ( "error preparing https server: %w" , err )
2019-03-14 09:30:04 +01:00
}
2022-02-15 16:04:09 +01:00
h3Server , err := newHTTP3Server ( ctx , configuration , httpsServer )
2021-01-07 14:48:04 +01:00
if err != nil {
2022-02-15 16:04:09 +01:00
return nil , fmt . Errorf ( "error preparing http3 server: %w" , err )
2021-01-07 14:48:04 +01:00
}
2022-03-17 11:02:08 -06:00
rt . SetHTTPSForwarder ( httpsServer . Forwarder )
2019-03-14 09:30:04 +01:00
tcpSwitcher := & tcp . HandlerSwitcher { }
2021-01-07 14:48:04 +01:00
tcpSwitcher . Switch ( rt )
2019-03-14 09:30:04 +01:00
return & TCPEntryPoint {
listener : listener ,
switcher : tcpSwitcher ,
transportConfiguration : configuration . Transport ,
tracker : tracker ,
httpServer : httpServer ,
httpsServer : httpsServer ,
2022-02-15 16:04:09 +01:00
http3Server : h3Server ,
2019-03-14 09:30:04 +01:00
} , nil
}
2020-02-11 01:26:04 +01:00
// Start starts the TCP server.
func ( e * TCPEntryPoint ) Start ( ctx context . Context ) {
2019-09-13 19:28:04 +02:00
logger := log . FromContext ( ctx )
2022-03-17 11:02:08 -06:00
logger . Debug ( "Starting TCP Server" )
2019-03-14 09:30:04 +01:00
2021-01-07 14:48:04 +01:00
if e . http3Server != nil {
go func ( ) { _ = e . http3Server . Start ( ) } ( )
}
2019-03-14 09:30:04 +01:00
for {
conn , err := e . listener . Accept ( )
if err != nil {
2019-09-13 19:28:04 +02:00
logger . Error ( err )
2020-11-06 09:26:03 +01:00
2022-08-09 17:36:08 +02:00
var opErr * net . OpError
if errors . As ( err , & opErr ) && opErr . Temporary ( ) {
continue
}
var urlErr * url . Error
if errors . As ( err , & urlErr ) && urlErr . Temporary ( ) {
2019-12-04 16:26:05 +01:00
continue
}
2020-11-06 09:26:03 +01:00
2020-08-17 12:02:03 +02:00
e . httpServer . Forwarder . errChan <- err
e . httpsServer . Forwarder . errChan <- err
2020-11-06 09:26:03 +01:00
2019-03-14 09:30:04 +01:00
return
}
2019-09-13 17:46:04 +02:00
writeCloser , err := writeCloser ( conn )
if err != nil {
panic ( err )
}
2024-04-08 17:16:04 +02:00
safe . Go ( func ( ) {
2024-04-11 15:48:04 +02:00
// Enforce read/write deadlines at the connection level,
// because when we're peeking the first byte to determine whether we are doing TLS,
// the deadlines at the server level are not taken into account.
if e . transportConfiguration . RespondingTimeouts . ReadTimeout > 0 {
err := writeCloser . SetReadDeadline ( time . Now ( ) . Add ( time . Duration ( e . transportConfiguration . RespondingTimeouts . ReadTimeout ) ) )
if err != nil {
logger . Errorf ( "Error while setting read deadline: %v" , err )
}
}
if e . transportConfiguration . RespondingTimeouts . WriteTimeout > 0 {
err = writeCloser . SetWriteDeadline ( time . Now ( ) . Add ( time . Duration ( e . transportConfiguration . RespondingTimeouts . WriteTimeout ) ) )
if err != nil {
logger . Errorf ( "Error while setting write deadline: %v" , err )
}
}
2019-09-13 17:46:04 +02:00
e . switcher . ServeTCP ( newTrackedConnection ( writeCloser , e . tracker ) )
2019-03-14 09:30:04 +01:00
} )
}
}
2020-05-11 12:06:07 +02:00
// Shutdown stops the TCP connections.
2019-03-14 09:30:04 +01:00
func ( e * TCPEntryPoint ) Shutdown ( ctx context . Context ) {
logger := log . FromContext ( ctx )
reqAcceptGraceTimeOut := time . Duration ( e . transportConfiguration . LifeCycle . RequestAcceptGraceTimeout )
if reqAcceptGraceTimeOut > 0 {
logger . Infof ( "Waiting %s for incoming requests to cease" , reqAcceptGraceTimeOut )
time . Sleep ( reqAcceptGraceTimeOut )
}
graceTimeOut := time . Duration ( e . transportConfiguration . LifeCycle . GraceTimeOut )
ctx , cancel := context . WithTimeout ( ctx , graceTimeOut )
logger . Debugf ( "Waiting %s seconds before killing connections." , graceTimeOut )
var wg sync . WaitGroup
2020-01-06 16:56:05 +01:00
2021-01-07 14:48:04 +01:00
shutdownServer := func ( server stoppable ) {
2020-01-06 16:56:05 +01:00
defer wg . Done ( )
err := server . Shutdown ( ctx )
if err == nil {
return
}
2020-11-06 09:26:03 +01:00
if errors . Is ( ctx . Err ( ) , context . DeadlineExceeded ) {
2020-01-06 16:56:05 +01:00
logger . Debugf ( "Server failed to shutdown within deadline because: %s" , err )
if err = server . Close ( ) ; err != nil {
logger . Error ( err )
}
return
}
logger . Error ( err )
// We expect Close to fail again because Shutdown most likely failed when trying to close a listener.
// We still call it however, to make sure that all connections get closed as well.
server . Close ( )
}
2019-03-14 09:30:04 +01:00
if e . httpServer . Server != nil {
wg . Add ( 1 )
2020-01-06 16:56:05 +01:00
go shutdownServer ( e . httpServer . Server )
2019-03-14 09:30:04 +01:00
}
if e . httpsServer . Server != nil {
wg . Add ( 1 )
2020-01-06 16:56:05 +01:00
go shutdownServer ( e . httpsServer . Server )
2021-01-07 14:48:04 +01:00
if e . http3Server != nil {
wg . Add ( 1 )
go shutdownServer ( e . http3Server )
}
2019-03-14 09:30:04 +01:00
}
if e . tracker != nil {
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
2020-01-06 16:56:05 +01:00
err := e . tracker . Shutdown ( ctx )
if err == nil {
return
}
2020-11-06 09:26:03 +01:00
if errors . Is ( ctx . Err ( ) , context . DeadlineExceeded ) {
2020-01-06 16:56:05 +01:00
logger . Debugf ( "Server failed to shutdown before deadline because: %s" , err )
2019-03-14 09:30:04 +01:00
}
2020-01-06 16:56:05 +01:00
e . tracker . Close ( )
2019-03-14 09:30:04 +01:00
} ( )
}
wg . Wait ( )
cancel ( )
}
2019-11-14 16:40:05 +01:00
// SwitchRouter switches the TCP router handler.
2022-03-17 11:02:08 -06:00
func ( e * TCPEntryPoint ) SwitchRouter ( rt * tcprouter . Router ) {
rt . SetHTTPForwarder ( e . httpServer . Forwarder )
2019-03-14 09:30:04 +01:00
2019-11-14 16:40:05 +01:00
httpHandler := rt . GetHTTPHandler ( )
2019-03-14 09:30:04 +01:00
if httpHandler == nil {
2019-11-14 16:40:05 +01:00
httpHandler = router . BuildDefaultHTTPRouter ( )
2019-03-14 09:30:04 +01:00
}
2019-11-14 16:40:05 +01:00
e . httpServer . Switcher . UpdateHandler ( httpHandler )
2022-03-17 11:02:08 -06:00
rt . SetHTTPSForwarder ( e . httpsServer . Forwarder )
2019-11-14 16:40:05 +01:00
httpsHandler := rt . GetHTTPSHandler ( )
2019-03-14 09:30:04 +01:00
if httpsHandler == nil {
2019-11-14 16:40:05 +01:00
httpsHandler = router . BuildDefaultHTTPRouter ( )
2019-03-14 09:30:04 +01:00
}
e . httpsServer . Switcher . UpdateHandler ( httpsHandler )
2019-11-14 16:40:05 +01:00
e . switcher . Switch ( rt )
2021-01-07 14:48:04 +01:00
if e . http3Server != nil {
e . http3Server . Switch ( rt )
}
2019-11-14 16:40:05 +01:00
}
// writeCloserWrapper wraps together a connection, and the concrete underlying
// connection type that was found to satisfy WriteCloser.
type writeCloserWrapper struct {
net . Conn
writeCloser tcp . WriteCloser
}
func ( c * writeCloserWrapper ) CloseWrite ( ) error {
return c . writeCloser . CloseWrite ( )
}
// writeCloser returns the given connection, augmented with the WriteCloser
// implementation, if any was found within the underlying conn.
func writeCloser ( conn net . Conn ) ( tcp . WriteCloser , error ) {
switch typedConn := conn . ( type ) {
2020-11-17 13:04:04 +01:00
case * proxyproto . Conn :
underlying , ok := typedConn . TCPConn ( )
if ! ok {
2024-02-07 17:14:07 +01:00
return nil , errors . New ( "underlying connection is not a tcp connection" )
2019-11-14 16:40:05 +01:00
}
return & writeCloserWrapper { writeCloser : underlying , Conn : typedConn } , nil
case * net . TCPConn :
return typedConn , nil
default :
return nil , fmt . Errorf ( "unknown connection type %T" , typedConn )
}
2019-03-14 09:30:04 +01:00
}
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections.
type tcpKeepAliveListener struct {
* net . TCPListener
}
func ( ln tcpKeepAliveListener ) Accept ( ) ( net . Conn , error ) {
tc , err := ln . AcceptTCP ( )
if err != nil {
return nil , err
}
2021-03-04 09:02:03 +01:00
if err := tc . SetKeepAlive ( true ) ; err != nil {
2019-03-14 09:30:04 +01:00
return nil , err
}
2021-03-04 09:02:03 +01:00
if err := tc . SetKeepAlivePeriod ( 3 * time . Minute ) ; err != nil {
2020-10-28 15:32:04 +01:00
// Some systems, such as OpenBSD, have no user-settable per-socket TCP
// keepalive options.
if ! errors . Is ( err , syscall . ENOPROTOOPT ) {
return nil , err
}
2019-03-14 09:30:04 +01:00
}
return tc , nil
}
func buildProxyProtocolListener ( ctx context . Context , entryPoint * static . EntryPoint , listener net . Listener ) ( net . Listener , error ) {
2024-04-11 15:48:04 +02:00
timeout := entryPoint . Transport . RespondingTimeouts . ReadTimeout
2024-01-02 22:02:05 +01:00
// proxyproto use 200ms if ReadHeaderTimeout is set to 0 and not no timeout
if timeout == 0 {
timeout = - 1
}
proxyListener := & proxyproto . Listener { Listener : listener , ReadHeaderTimeout : time . Duration ( timeout ) }
2020-11-17 13:04:04 +01:00
2019-03-14 09:30:04 +01:00
if entryPoint . ProxyProtocol . Insecure {
2020-11-17 13:04:04 +01:00
log . FromContext ( ctx ) . Infof ( "Enabling ProxyProtocol without trusted IPs: Insecure" )
return proxyListener , nil
}
2019-03-14 09:30:04 +01:00
2020-11-17 13:04:04 +01:00
checker , err := ip . NewChecker ( entryPoint . ProxyProtocol . TrustedIPs )
if err != nil {
return nil , err
}
proxyListener . Policy = func ( upstream net . Addr ) ( proxyproto . Policy , error ) {
ipAddr , ok := upstream . ( * net . TCPAddr )
if ! ok {
return proxyproto . REJECT , fmt . Errorf ( "type error %v" , upstream )
}
2019-03-14 09:30:04 +01:00
2020-11-17 13:04:04 +01:00
if ! checker . ContainsIP ( ipAddr . IP ) {
log . FromContext ( ctx ) . Debugf ( "IP %s is not in trusted IPs list, ignoring ProxyProtocol Headers and bypass connection" , ipAddr . IP )
return proxyproto . IGNORE , nil
2019-03-14 09:30:04 +01:00
}
2020-11-17 13:04:04 +01:00
return proxyproto . USE , nil
2019-03-14 09:30:04 +01:00
}
log . FromContext ( ctx ) . Infof ( "Enabling ProxyProtocol for trusted IPs %v" , entryPoint . ProxyProtocol . TrustedIPs )
2020-11-17 13:04:04 +01:00
return proxyListener , nil
2019-03-14 09:30:04 +01:00
}
func buildListener ( ctx context . Context , entryPoint * static . EntryPoint ) ( net . Listener , error ) {
2020-02-11 01:26:04 +01:00
listener , err := net . Listen ( "tcp" , entryPoint . GetAddress ( ) )
2019-03-14 09:30:04 +01:00
if err != nil {
2020-05-11 12:06:07 +02:00
return nil , fmt . Errorf ( "error opening listener: %w" , err )
2019-03-14 09:30:04 +01:00
}
listener = tcpKeepAliveListener { listener . ( * net . TCPListener ) }
if entryPoint . ProxyProtocol != nil {
listener , err = buildProxyProtocolListener ( ctx , entryPoint , listener )
if err != nil {
2020-05-11 12:06:07 +02:00
return nil , fmt . Errorf ( "error creating proxy protocol listener: %w" , err )
2019-03-14 09:30:04 +01:00
}
}
return listener , nil
}
func newConnectionTracker ( ) * connectionTracker {
return & connectionTracker {
conns : make ( map [ net . Conn ] struct { } ) ,
}
}
type connectionTracker struct {
conns map [ net . Conn ] struct { }
lock sync . RWMutex
}
2020-05-11 12:06:07 +02:00
// AddConnection add a connection in the tracked connections list.
2019-03-14 09:30:04 +01:00
func ( c * connectionTracker ) AddConnection ( conn net . Conn ) {
c . lock . Lock ( )
defer c . lock . Unlock ( )
c . conns [ conn ] = struct { } { }
}
2020-05-11 12:06:07 +02:00
// RemoveConnection remove a connection from the tracked connections list.
2019-03-14 09:30:04 +01:00
func ( c * connectionTracker ) RemoveConnection ( conn net . Conn ) {
c . lock . Lock ( )
defer c . lock . Unlock ( )
delete ( c . conns , conn )
}
2019-03-15 10:04:05 +01:00
func ( c * connectionTracker ) isEmpty ( ) bool {
c . lock . RLock ( )
defer c . lock . RUnlock ( )
return len ( c . conns ) == 0
}
2020-05-11 12:06:07 +02:00
// Shutdown wait for the connection closing.
2019-03-14 09:30:04 +01:00
func ( c * connectionTracker ) Shutdown ( ctx context . Context ) error {
ticker := time . NewTicker ( 500 * time . Millisecond )
defer ticker . Stop ( )
for {
2019-03-15 10:04:05 +01:00
if c . isEmpty ( ) {
2019-03-14 09:30:04 +01:00
return nil
}
select {
case <- ctx . Done ( ) :
return ctx . Err ( )
case <- ticker . C :
}
}
}
2020-05-11 12:06:07 +02:00
// Close close all the connections in the tracked connections list.
2019-03-14 09:30:04 +01:00
func ( c * connectionTracker ) Close ( ) {
2019-03-21 15:54:07 +01:00
c . lock . Lock ( )
defer c . lock . Unlock ( )
2019-03-14 09:30:04 +01:00
for conn := range c . conns {
if err := conn . Close ( ) ; err != nil {
log . WithoutContext ( ) . Errorf ( "Error while closing connection: %v" , err )
}
2019-03-21 15:54:07 +01:00
delete ( c . conns , conn )
2019-03-14 09:30:04 +01:00
}
}
2021-01-07 14:48:04 +01:00
type stoppable interface {
2023-11-17 01:50:06 +01:00
Shutdown ( ctx context . Context ) error
2019-03-14 09:30:04 +01:00
Close ( ) error
2021-01-07 14:48:04 +01:00
}
type stoppableServer interface {
stoppable
2019-03-14 09:30:04 +01:00
Serve ( listener net . Listener ) error
}
type httpServer struct {
Server stoppableServer
Forwarder * httpForwarder
Switcher * middlewares . HTTPHandlerSwitcher
}
2022-02-14 17:18:08 +01:00
func createHTTPServer ( ctx context . Context , ln net . Listener , configuration * static . EntryPoint , withH2c bool , reqDecorator * requestdecorator . RequestDecorator ) ( * httpServer , error ) {
2022-04-04 11:46:07 +02:00
if configuration . HTTP2 . MaxConcurrentStreams < 0 {
return nil , errors . New ( "max concurrent streams value must be greater than or equal to zero" )
}
2019-11-14 16:40:05 +01:00
httpSwitcher := middlewares . NewHandlerSwitcher ( router . BuildDefaultHTTPRouter ( ) )
2019-07-01 15:08:04 +02:00
2022-02-14 17:18:08 +01:00
next , err := alice . New ( requestdecorator . WrapHandler ( reqDecorator ) ) . Then ( httpSwitcher )
if err != nil {
return nil , err
}
2019-07-01 15:08:04 +02:00
var handler http . Handler
handler , err = forwardedheaders . NewXForwarded (
2019-03-14 09:30:04 +01:00
configuration . ForwardedHeaders . Insecure ,
configuration . ForwardedHeaders . TrustedIPs ,
2024-09-16 11:10:04 +02:00
configuration . ForwardedHeaders . Connection ,
2022-02-14 17:18:08 +01:00
next )
2019-03-14 09:30:04 +01:00
if err != nil {
return nil , err
}
2023-11-16 16:54:07 +01:00
handler = denyFragment ( handler )
2023-06-15 18:20:06 +02:00
if configuration . HTTP . EncodeQuerySemicolons {
handler = encodeQuerySemicolons ( handler )
} else {
handler = http . AllowQuerySemicolons ( handler )
}
2022-06-27 15:16:08 +02:00
2024-11-18 16:56:04 +08:00
debugConnection := os . Getenv ( debugConnectionEnv ) != ""
if debugConnection || ( configuration . Transport != nil && ( configuration . Transport . KeepAliveMaxTime > 0 || configuration . Transport . KeepAliveMaxRequests > 0 ) ) {
handler = newKeepAliveMiddleware ( handler , configuration . Transport . KeepAliveMaxRequests , configuration . Transport . KeepAliveMaxTime )
}
2019-03-14 09:30:04 +01:00
if withH2c {
2022-04-04 11:46:07 +02:00
handler = h2c . NewHandler ( handler , & http2 . Server {
MaxConcurrentStreams : uint32 ( configuration . HTTP2 . MaxConcurrentStreams ) ,
} )
2019-07-01 15:08:04 +02:00
}
serverHTTP := & http . Server {
2020-01-06 16:56:05 +01:00
Handler : handler ,
ErrorLog : httpServerLogger ,
2024-04-11 15:48:04 +02:00
ReadTimeout : time . Duration ( configuration . Transport . RespondingTimeouts . ReadTimeout ) ,
WriteTimeout : time . Duration ( configuration . Transport . RespondingTimeouts . WriteTimeout ) ,
IdleTimeout : time . Duration ( configuration . Transport . RespondingTimeouts . IdleTimeout ) ,
2019-03-14 09:30:04 +01:00
}
2024-01-02 16:40:06 +01:00
if debugConnection || ( configuration . Transport != nil && ( configuration . Transport . KeepAliveMaxTime > 0 || configuration . Transport . KeepAliveMaxRequests > 0 ) ) {
serverHTTP . ConnContext = func ( ctx context . Context , c net . Conn ) context . Context {
cState := & connState { Start : time . Now ( ) }
if debugConnection {
clientConnectionStatesMu . Lock ( )
clientConnectionStates [ getConnKey ( c ) ] = cState
clientConnectionStatesMu . Unlock ( )
}
return context . WithValue ( ctx , connStateKey , cState )
}
if debugConnection {
serverHTTP . ConnState = func ( c net . Conn , state http . ConnState ) {
clientConnectionStatesMu . Lock ( )
if clientConnectionStates [ getConnKey ( c ) ] != nil {
clientConnectionStates [ getConnKey ( c ) ] . State = state . String ( )
}
clientConnectionStatesMu . Unlock ( )
}
}
}
2019-03-14 09:30:04 +01:00
2024-02-06 17:34:07 +01:00
prevConnContext := serverHTTP . ConnContext
serverHTTP . ConnContext = func ( ctx context . Context , c net . Conn ) context . Context {
// This adds an empty struct in order to store a RoundTripper in the ConnContext in case of Kerberos or NTLM.
ctx = service . AddTransportOnContext ( ctx )
if prevConnContext != nil {
return prevConnContext ( ctx , c )
}
return ctx
}
2022-04-04 11:46:07 +02:00
// ConfigureServer configures HTTP/2 with the MaxConcurrentStreams option for the given server.
// Also keeping behavior the same as
// https://cs.opensource.google/go/go/+/refs/tags/go1.17.7:src/net/http/server.go;l=3262
if ! strings . Contains ( os . Getenv ( "GODEBUG" ) , "http2server=0" ) {
err = http2 . ConfigureServer ( serverHTTP , & http2 . Server {
MaxConcurrentStreams : uint32 ( configuration . HTTP2 . MaxConcurrentStreams ) ,
NewWriteScheduler : func ( ) http2 . WriteScheduler { return http2 . NewPriorityWriteScheduler ( nil ) } ,
} )
if err != nil {
return nil , fmt . Errorf ( "configure HTTP/2 server: %w" , err )
}
}
2019-03-14 09:30:04 +01:00
listener := newHTTPForwarder ( ln )
go func ( ) {
err := serverHTTP . Serve ( listener )
2021-01-07 14:48:04 +01:00
if err != nil && ! errors . Is ( err , http . ErrServerClosed ) {
2019-09-13 19:28:04 +02:00
log . FromContext ( ctx ) . Errorf ( "Error while starting server: %v" , err )
2019-03-14 09:30:04 +01:00
}
} ( )
return & httpServer {
Server : serverHTTP ,
Forwarder : listener ,
Switcher : httpSwitcher ,
} , nil
}
2024-01-02 16:40:06 +01:00
func getConnKey ( conn net . Conn ) string {
return fmt . Sprintf ( "%s => %s" , conn . RemoteAddr ( ) , conn . LocalAddr ( ) )
}
2019-09-13 17:46:04 +02:00
func newTrackedConnection ( conn tcp . WriteCloser , tracker * connectionTracker ) * trackedConnection {
2019-03-14 09:30:04 +01:00
tracker . AddConnection ( conn )
return & trackedConnection {
2019-09-13 17:46:04 +02:00
WriteCloser : conn ,
tracker : tracker ,
2019-03-14 09:30:04 +01:00
}
}
type trackedConnection struct {
tracker * connectionTracker
2019-09-13 17:46:04 +02:00
tcp . WriteCloser
2019-03-14 09:30:04 +01:00
}
func ( t * trackedConnection ) Close ( ) error {
2019-09-13 17:46:04 +02:00
t . tracker . RemoveConnection ( t . WriteCloser )
return t . WriteCloser . Close ( )
2019-03-14 09:30:04 +01:00
}
2023-06-15 18:20:06 +02:00
// This function is inspired by http.AllowQuerySemicolons.
func encodeQuerySemicolons ( h http . Handler ) http . Handler {
return http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
if strings . Contains ( req . URL . RawQuery , ";" ) {
r2 := new ( http . Request )
* r2 = * req
r2 . URL = new ( url . URL )
* r2 . URL = * req . URL
r2 . URL . RawQuery = strings . ReplaceAll ( req . URL . RawQuery , ";" , "%3B" )
// Because the reverse proxy director is building query params from requestURI it needs to be updated as well.
r2 . RequestURI = r2 . URL . RequestURI ( )
h . ServeHTTP ( rw , r2 )
} else {
h . ServeHTTP ( rw , req )
}
} )
}
2023-11-16 16:54:07 +01:00
// When go receives an HTTP request, it assumes the absence of fragment URL.
// However, it is still possible to send a fragment in the request.
// In this case, Traefik will encode the '#' character, altering the request's intended meaning.
// To avoid this behavior, the following function rejects requests that include a fragment in the URL.
func denyFragment ( h http . Handler ) http . Handler {
return http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
if strings . Contains ( req . URL . RawPath , "#" ) {
log . WithoutContext ( ) . Debugf ( "Rejecting request because it contains a fragment in the URL path: %s" , req . URL . RawPath )
rw . WriteHeader ( http . StatusBadRequest )
return
}
h . ServeHTTP ( rw , req )
} )
}