2
0
mirror of https://github.com/soheilhy/cmux.git synced 2024-09-19 18:45:48 +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:
Soheil Hassas Yeganeh 2015-08-30 21:07:48 -04:00
parent d47edebeb9
commit b90740dfa9
2 changed files with 26 additions and 7 deletions

20
cmux.go
View File

@ -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
} }

View File

@ -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{})
}