forked from Mxmilu666/frp
fix(vhost): close pending connections when a route listener closes (#5592)
When an HTTPS or tcpmux route listener is closed after a connection has been resolved to it but before the handoff, Muxer.handle clears the deadline and then sends on the closed accept channel. The recovered panic returns without closing the connection, so the peer waits until its own read timeout. Add a done channel and sync.Once to Listener. Close closes done and is idempotent; handle and Accept select on accept/done, and a pending connection is closed when done is closed. Connections already handed to Accept keep their current ownership. Remove the PanicToError-based control flow. Cover the pending handoff with net.Pipe and real TCP tests, plus an idempotent Close test. Fixes #5573
This commit is contained in:
+17
-10
@@ -17,10 +17,9 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
|
||||
"github.com/fatedier/frp/pkg/util/log"
|
||||
netpkg "github.com/fatedier/frp/pkg/util/net"
|
||||
"github.com/fatedier/frp/pkg/util/xlog"
|
||||
@@ -138,6 +137,7 @@ func (v *Muxer) Listen(ctx context.Context, cfg *RouteConfig) (l *Listener, err
|
||||
password: cfg.Password,
|
||||
mux: v,
|
||||
accept: make(chan net.Conn),
|
||||
done: make(chan struct{}),
|
||||
ctx: ctx,
|
||||
}
|
||||
err = v.registryRouter.Add(cfg.Domain, cfg.Location, cfg.RouteByHTTPUser, l)
|
||||
@@ -215,11 +215,12 @@ func (v *Muxer) handle(c net.Conn) {
|
||||
c = sConn
|
||||
|
||||
xl.Debugf("new request host [%s] path [%s] httpUser [%s]", name, path, httpUser)
|
||||
err = errors.PanicToError(func() {
|
||||
l.accept <- c
|
||||
})
|
||||
if err != nil {
|
||||
select {
|
||||
case l.accept <- c:
|
||||
// The connection is now owned by the Accept caller.
|
||||
case <-l.done:
|
||||
xl.Warnf("listener is already closed, ignore this request")
|
||||
_ = c.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,13 +233,17 @@ type Listener struct {
|
||||
password string
|
||||
mux *Muxer // for closing Muxer
|
||||
accept chan net.Conn
|
||||
done chan struct{}
|
||||
closeOnce sync.Once
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
xl := xlog.FromContextSafe(l.ctx)
|
||||
conn, ok := <-l.accept
|
||||
if !ok {
|
||||
var conn net.Conn
|
||||
select {
|
||||
case conn = <-l.accept:
|
||||
case <-l.done:
|
||||
return nil, fmt.Errorf("listener closed")
|
||||
}
|
||||
|
||||
@@ -258,8 +263,10 @@ func (l *Listener) Accept() (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (l *Listener) Close() error {
|
||||
l.mux.registryRouter.Del(l.name, l.location, l.routeByHTTPUser)
|
||||
close(l.accept)
|
||||
l.closeOnce.Do(func() {
|
||||
l.mux.registryRouter.Del(l.name, l.location, l.routeByHTTPUser)
|
||||
close(l.done)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user