fix(dialer): detect dead QUIC sessions in ICMP/QUIC dialer caches
When a QUIC session's MaxIdleTimeout expires, the underlying *quic.Conn dies but the dead quicSession stays in the sessions map. The next Dial() finds it and calls session.GetConn() which calls OpenStreamSync(context.Background()) against the dead connection — blocking indefinitely. Add IsClosed() to both quicSession types (using the session context's Done() channel) and add the liveness guard in both Dial() methods, matching the existing pattern in KCP and MASQUE dialers. Fixes go-gost/gost#225
This commit is contained in:
@@ -23,6 +23,15 @@ func (session *quicSession) GetConn() (*quicConn, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (session *quicSession) IsClosed() bool {
|
||||||
|
select {
|
||||||
|
case <-session.session.Context().Done():
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (session *quicSession) Close() error {
|
func (session *quicSession) Close() error {
|
||||||
return session.session.CloseWithError(quic.ApplicationErrorCode(0), "closed")
|
return session.session.CloseWithError(quic.ApplicationErrorCode(0), "closed")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ func (d *icmpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
defer d.sessionMutex.Unlock()
|
defer d.sessionMutex.Unlock()
|
||||||
|
|
||||||
session, ok := d.sessions[addr]
|
session, ok := d.sessions[addr]
|
||||||
|
if session != nil && session.IsClosed() {
|
||||||
|
delete(d.sessions, addr) // session is dead
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
options := &dialer.DialOptions{}
|
options := &dialer.DialOptions{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
|||||||
@@ -23,6 +23,15 @@ func (session *quicSession) GetConn() (*quicConn, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (session *quicSession) IsClosed() bool {
|
||||||
|
select {
|
||||||
|
case <-session.session.Context().Done():
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (session *quicSession) Close() error {
|
func (session *quicSession) Close() error {
|
||||||
return session.session.CloseWithError(quic.ApplicationErrorCode(0), "closed")
|
return session.session.CloseWithError(quic.ApplicationErrorCode(0), "closed")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ func (d *quicDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
defer d.sessionMutex.Unlock()
|
defer d.sessionMutex.Unlock()
|
||||||
|
|
||||||
session, ok := d.sessions[addr]
|
session, ok := d.sessions[addr]
|
||||||
|
if session != nil && session.IsClosed() {
|
||||||
|
delete(d.sessions, addr) // session is dead
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
options := &dialer.DialOptions{}
|
options := &dialer.DialOptions{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
|||||||
Reference in New Issue
Block a user