mirror of
https://github.com/soheilhy/cmux.git
synced 2024-11-10 03:31:52 +08:00
Fix send on closed channel
If a matcher go-routine mateches a connection after the root listener was closed, cmux will panic since all connection channels will be closed. This commit avoids closing connection channels but signals the matcher go-routines that the listener is closed via a done channel. Tested: Added a test case for this issue.
This commit is contained in:
parent
d47edebeb9
commit
b90740dfa9
20
cmux.go
20
cmux.go
@ -80,7 +80,8 @@ type cMux struct {
|
|||||||
func (m *cMux) Match(matchers ...Matcher) (l net.Listener) {
|
func (m *cMux) Match(matchers ...Matcher) (l net.Listener) {
|
||||||
ml := muxListener{
|
ml := muxListener{
|
||||||
Listener: m.root,
|
Listener: m.root,
|
||||||
cch: make(chan net.Conn, m.bufLen),
|
connc: make(chan net.Conn, m.bufLen),
|
||||||
|
donec: make(chan struct{}),
|
||||||
}
|
}
|
||||||
m.sls = append(m.sls, matchersListener{ss: matchers, l: ml})
|
m.sls = append(m.sls, matchersListener{ss: matchers, l: ml})
|
||||||
return ml
|
return ml
|
||||||
@ -89,7 +90,7 @@ func (m *cMux) Match(matchers ...Matcher) (l net.Listener) {
|
|||||||
func (m *cMux) Serve() error {
|
func (m *cMux) Serve() error {
|
||||||
defer func() {
|
defer func() {
|
||||||
for _, sl := range m.sls {
|
for _, sl := range m.sls {
|
||||||
close(sl.l.cch)
|
close(sl.l.donec)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -109,14 +110,18 @@ func (m *cMux) Serve() error {
|
|||||||
func (m *cMux) serve(c net.Conn) {
|
func (m *cMux) serve(c net.Conn) {
|
||||||
muc := newMuxConn(c)
|
muc := newMuxConn(c)
|
||||||
matched := false
|
matched := false
|
||||||
outer:
|
|
||||||
for _, sl := range m.sls {
|
for _, sl := range m.sls {
|
||||||
for _, s := range sl.ss {
|
for _, s := range sl.ss {
|
||||||
matched = s(muc.sniffer())
|
matched = s(muc.sniffer())
|
||||||
muc.reset()
|
muc.reset()
|
||||||
if matched {
|
if matched {
|
||||||
sl.l.cch <- muc
|
select {
|
||||||
break outer
|
// TODO(soheil): threre is a possiblity of having unclosed connection.
|
||||||
|
case sl.l.connc <- muc:
|
||||||
|
case <-sl.l.donec:
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,11 +153,12 @@ func (m *cMux) handleErr(err error) bool {
|
|||||||
|
|
||||||
type muxListener struct {
|
type muxListener struct {
|
||||||
net.Listener
|
net.Listener
|
||||||
cch chan net.Conn
|
connc chan net.Conn
|
||||||
|
donec chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l muxListener) Accept() (c net.Conn, err error) {
|
func (l muxListener) Accept() (c net.Conn, err error) {
|
||||||
c, ok := <-l.cch
|
c, ok := <-l.connc
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, ErrListenerClosed
|
return nil, ErrListenerClosed
|
||||||
}
|
}
|
||||||
|
13
cmux_test.go
13
cmux_test.go
@ -168,3 +168,16 @@ func TestErrorHandler(t *testing.T) {
|
|||||||
t.Error("rpc got a response")
|
t.Error("rpc got a response")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type closerConn struct {
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c closerConn) Close() error { return nil }
|
||||||
|
|
||||||
|
func TestClosed(t *testing.T) {
|
||||||
|
mux := &cMux{}
|
||||||
|
lis := mux.Match(Any()).(muxListener)
|
||||||
|
close(lis.donec)
|
||||||
|
mux.serve(closerConn{})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user