1
0
Fork 0

On client CloseWrite, do CloseWrite instead of Close for backend

Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
This commit is contained in:
Julien Salleyron 2019-09-13 17:46:04 +02:00 committed by Traefiker Bot
parent 401b3afa3b
commit b55be9fdea
25 changed files with 393 additions and 36 deletions

View file

@ -37,7 +37,7 @@ func newHTTPForwarder(ln net.Listener) *httpForwarder {
}
// ServeTCP uses the connection to serve it later in "Accept"
func (h *httpForwarder) ServeTCP(conn net.Conn) {
func (h *httpForwarder) ServeTCP(conn tcp.WriteCloser) {
h.connChan <- conn
}
@ -99,7 +99,36 @@ func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint) (*T
}, nil
}
// 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) {
case *proxyprotocol.Conn:
underlying, err := writeCloser(typedConn.Conn)
if err != nil {
return nil, err
}
return &writeCloserWrapper{writeCloser: underlying, Conn: typedConn}, nil
case *net.TCPConn:
return typedConn, nil
default:
return nil, fmt.Errorf("unknown connection type %T", typedConn)
}
}
func (e *TCPEntryPoint) startTCP(ctx context.Context) {
log.FromContext(ctx).Debugf("Start TCP Server")
for {
@ -109,8 +138,13 @@ func (e *TCPEntryPoint) startTCP(ctx context.Context) {
return
}
writeCloser, err := writeCloser(conn)
if err != nil {
panic(err)
}
safe.Go(func() {
e.switcher.ServeTCP(newTrackedConnection(conn, e.tracker))
e.switcher.ServeTCP(newTrackedConnection(writeCloser, e.tracker))
})
}
}
@ -374,20 +408,20 @@ func createHTTPServer(ln net.Listener, configuration *static.EntryPoint, withH2c
}, nil
}
func newTrackedConnection(conn net.Conn, tracker *connectionTracker) *trackedConnection {
func newTrackedConnection(conn tcp.WriteCloser, tracker *connectionTracker) *trackedConnection {
tracker.AddConnection(conn)
return &trackedConnection{
Conn: conn,
tracker: tracker,
WriteCloser: conn,
tracker: tracker,
}
}
type trackedConnection struct {
tracker *connectionTracker
net.Conn
tcp.WriteCloser
}
func (t *trackedConnection) Close() error {
t.tracker.RemoveConnection(t.Conn)
return t.Conn.Close()
t.tracker.RemoveConnection(t.WriteCloser)
return t.WriteCloser.Close()
}

View file

@ -113,7 +113,7 @@ func TestShutdownTCPConn(t *testing.T) {
go entryPoint.startTCP(context.Background())
router := &tcp.Router{}
router.AddCatchAllNoTLS(tcp.HandlerFunc(func(conn net.Conn) {
router.AddCatchAllNoTLS(tcp.HandlerFunc(func(conn tcp.WriteCloser) {
_, err := http.ReadRequest(bufio.NewReader(conn))
require.NoError(t, err)
time.Sleep(1 * time.Second)

View file

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"time"
"github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/log"
@ -44,13 +45,19 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
loadBalancer := tcp.NewRRLoadBalancer()
if conf.LoadBalancer.TerminationDelay == nil {
defaultTerminationDelay := 100
conf.LoadBalancer.TerminationDelay = &defaultTerminationDelay
}
duration := time.Millisecond * time.Duration(*conf.LoadBalancer.TerminationDelay)
for name, server := range conf.LoadBalancer.Servers {
if _, _, err := net.SplitHostPort(server.Address); err != nil {
logger.Errorf("In service %q: %v", serviceQualifiedName, err)
continue
}
handler, err := tcp.NewProxy(server.Address)
handler, err := tcp.NewProxy(server.Address, duration)
if err != nil {
logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err)
continue