server: add error to OnSessionClose()

This commit is contained in:
aler9
2021-05-04 15:51:35 +02:00
committed by Alessandro Ros
parent 6f749e6ba8
commit 9d42a63102
9 changed files with 99 additions and 79 deletions

View File

@@ -38,7 +38,7 @@ func (sh *serverHandler) OnSessionOpen(ss *gortsplib.ServerSession) {
} }
// called when a session is closed. // called when a session is closed.
func (sh *serverHandler) OnSessionClose(ss *gortsplib.ServerSession) { func (sh *serverHandler) OnSessionClose(ss *gortsplib.ServerSession, err error) {
log.Printf("session closed") log.Printf("session closed")
sh.mutex.Lock() sh.mutex.Lock()

View File

@@ -37,7 +37,7 @@ func (sh *serverHandler) OnSessionOpen(ss *gortsplib.ServerSession) {
} }
// called when a session is closed. // called when a session is closed.
func (sh *serverHandler) OnSessionClose(ss *gortsplib.ServerSession) { func (sh *serverHandler) OnSessionClose(ss *gortsplib.ServerSession, err error) {
log.Printf("session closed") log.Printf("session closed")
sh.mutex.Lock() sh.mutex.Lock()

View File

@@ -7,6 +7,22 @@ import (
"github.com/aler9/gortsplib/pkg/headers" "github.com/aler9/gortsplib/pkg/headers"
) )
// ErrServerTerminated is an error that can be returned by a server.
type ErrServerTerminated struct{}
// Error implements the error interface.
func (e ErrServerTerminated) Error() string {
return "terminated"
}
// ErrServerSessionTimedOut is an error that can be returned by a server.
type ErrServerSessionTimedOut struct{}
// Error implements the error interface.
func (e ErrServerSessionTimedOut) Error() string {
return "timed out"
}
// ErrServerTCPFramesEnable is an error that can be returned by a server. // ErrServerTCPFramesEnable is an error that can be returned by a server.
type ErrServerTCPFramesEnable struct{} type ErrServerTCPFramesEnable struct{}
@@ -192,11 +208,11 @@ func (e ErrServerLinkedToOtherSession) Error() string {
return "connection is linked to another session" return "connection is linked to another session"
} }
// ErrServerTeardown is an error that can be returned by a server. // ErrServerSessionTeardown is an error that can be returned by a server.
type ErrServerTeardown struct{} type ErrServerSessionTeardown struct{}
// Error implements the error interface. // Error implements the error interface.
func (e ErrServerTeardown) Error() string { func (e ErrServerSessionTeardown) Error() string {
return "teardown" return "teardown"
} }

View File

@@ -500,7 +500,7 @@ func TestServerPublish(t *testing.T) {
onSessionOpen: func(ss *ServerSession) { onSessionOpen: func(ss *ServerSession) {
close(sessionOpened) close(sessionOpened)
}, },
onSessionClose: func(ss *ServerSession) { onSessionClose: func(ss *ServerSession, err error) {
close(sessionClosed) close(sessionClosed)
}, },
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) { onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
@@ -997,7 +997,7 @@ func TestServerPublishErrorTimeout(t *testing.T) {
s := &Server{ s := &Server{
Handler: &testServerHandler{ Handler: &testServerHandler{
onSessionClose: func(ss *ServerSession) { onSessionClose: func(ss *ServerSession, err error) {
close(sessionClosed) close(sessionClosed)
}, },
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) { onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {

View File

@@ -288,7 +288,7 @@ func TestServerRead(t *testing.T) {
onSessionOpen: func(ss *ServerSession) { onSessionOpen: func(ss *ServerSession) {
close(sessionOpened) close(sessionOpened)
}, },
onSessionClose: func(ss *ServerSession) { onSessionClose: func(ss *ServerSession, err error) {
close(sessionClosed) close(sessionClosed)
}, },
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) { onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {

View File

@@ -19,7 +19,7 @@ type testServerHandler struct {
onConnOpen func(*ServerConn) onConnOpen func(*ServerConn)
onConnClose func(*ServerConn, error) onConnClose func(*ServerConn, error)
onSessionOpen func(*ServerSession) onSessionOpen func(*ServerSession)
onSessionClose func(*ServerSession) onSessionClose func(*ServerSession, error)
onDescribe func(*ServerHandlerOnDescribeCtx) (*base.Response, []byte, error) onDescribe func(*ServerHandlerOnDescribeCtx) (*base.Response, []byte, error)
onAnnounce func(*ServerHandlerOnAnnounceCtx) (*base.Response, error) onAnnounce func(*ServerHandlerOnAnnounceCtx) (*base.Response, error)
onSetup func(*ServerHandlerOnSetupCtx) (*base.Response, error) onSetup func(*ServerHandlerOnSetupCtx) (*base.Response, error)
@@ -49,9 +49,9 @@ func (sh *testServerHandler) OnSessionOpen(ss *ServerSession) {
} }
} }
func (sh *testServerHandler) OnSessionClose(ss *ServerSession) { func (sh *testServerHandler) OnSessionClose(ss *ServerSession, err error) {
if sh.onSessionClose != nil { if sh.onSessionClose != nil {
sh.onSessionClose(ss) sh.onSessionClose(ss, err)
} }
} }
@@ -211,7 +211,7 @@ func TestServerHighLevelPublishRead(t *testing.T) {
s := &Server{ s := &Server{
Handler: &testServerHandler{ Handler: &testServerHandler{
onSessionClose: func(ss *ServerSession) { onSessionClose: func(ss *ServerSession, err error) {
mutex.Lock() mutex.Lock()
defer mutex.Unlock() defer mutex.Unlock()

View File

@@ -168,25 +168,28 @@ func (sc *ServerConn) run() {
}() }()
}() }()
var err error err := func() error {
select { select {
case err = <-readDone: case err := <-readDone:
if sc.tcpFrameEnabled { if sc.tcpFrameEnabled {
sc.tcpFrameWriteBuffer.Close() sc.tcpFrameWriteBuffer.Close()
<-sc.tcpFrameBackgroundWriteDone <-sc.tcpFrameBackgroundWriteDone
} }
sc.nconn.Close() sc.nconn.Close()
sc.s.connClose <- sc sc.s.connClose <- sc
<-sc.terminate <-sc.terminate
return err
case <-sc.terminate: case <-sc.terminate:
if sc.tcpFrameEnabled { if sc.tcpFrameEnabled {
sc.tcpFrameWriteBuffer.Close() sc.tcpFrameWriteBuffer.Close()
<-sc.tcpFrameBackgroundWriteDone <-sc.tcpFrameBackgroundWriteDone
}
sc.nconn.Close()
<-readDone
return liberrors.ErrServerTerminated{}
} }
sc.nconn.Close() }()
err = <-readDone
}
if sc.tcpFrameEnabled { if sc.tcpFrameEnabled {
sc.s.sessionClose <- sc.tcpFrameLinkedSession sc.s.sessionClose <- sc.tcpFrameLinkedSession

View File

@@ -26,7 +26,7 @@ type ServerHandlerOnSessionOpen interface {
// ServerHandlerOnSessionClose can be implemented by a ServerHandler. // ServerHandlerOnSessionClose can be implemented by a ServerHandler.
type ServerHandlerOnSessionClose interface { type ServerHandlerOnSessionClose interface {
OnSessionClose(*ServerSession) OnSessionClose(*ServerSession, error)
} }
// ServerHandlerOnRequest can be implemented by a ServerHandler. // ServerHandlerOnRequest can be implemented by a ServerHandler.

View File

@@ -210,64 +210,65 @@ func (ss *ServerSession) run() {
receiverReportTicker := time.NewTicker(ss.s.receiverReportPeriod) receiverReportTicker := time.NewTicker(ss.s.receiverReportPeriod)
defer receiverReportTicker.Stop() defer receiverReportTicker.Stop()
outer: err := func() error {
for { for {
select { select {
case req := <-ss.request: case req := <-ss.request:
res, err := ss.handleRequest(req.sc, req.req) res, err := ss.handleRequest(req.sc, req.req)
ss.lastRequestTime = time.Now() ss.lastRequestTime = time.Now()
if res.StatusCode == base.StatusOK { if res.StatusCode == base.StatusOK {
if res.Header == nil { if res.Header == nil {
res.Header = make(base.Header) res.Header = make(base.Header)
}
res.Header["Session"] = base.HeaderValue{ss.id}
} }
res.Header["Session"] = base.HeaderValue{ss.id}
}
if _, ok := err.(liberrors.ErrServerTeardown); ok { if _, ok := err.(liberrors.ErrServerSessionTeardown); ok {
req.res <- requestRes{res, nil} req.res <- requestRes{res, nil}
break outer return liberrors.ErrServerSessionTeardown{}
} }
req.res <- requestRes{res, err} req.res <- requestRes{res, err}
case <-checkTimeoutTicker.C:
switch {
// in case of record and UDP, timeout happens when no frames are being received
case ss.state == ServerSessionStateRecord && *ss.setupProtocol == StreamProtocolUDP:
now := time.Now()
lft := atomic.LoadInt64(ss.udpLastFrameTime)
if now.Sub(time.Unix(lft, 0)) >= ss.s.ReadTimeout {
return liberrors.ErrServerSessionTimedOut{}
}
// in case there's a linked TCP connection, timeout is handled in the connection
case ss.linkedConn != nil:
// otherwise, timeout happens when no requests arrives
default:
now := time.Now()
if now.Sub(ss.lastRequestTime) >= serverSessionCloseAfterNoRequestsFor {
return liberrors.ErrServerSessionTimedOut{}
}
}
case <-receiverReportTicker.C:
if ss.state != ServerSessionStateRecord {
continue
}
case <-checkTimeoutTicker.C:
switch {
// in case of record and UDP, timeout happens when no frames are being received
case ss.state == ServerSessionStateRecord && *ss.setupProtocol == StreamProtocolUDP:
now := time.Now() now := time.Now()
lft := atomic.LoadInt64(ss.udpLastFrameTime) for trackID, track := range ss.announcedTracks {
if now.Sub(time.Unix(lft, 0)) >= ss.s.ReadTimeout { r := track.rtcpReceiver.Report(now)
break outer ss.WriteFrame(trackID, StreamTypeRTCP, r)
} }
// in case there's a linked TCP connection, timeout is handled in the connection case <-ss.terminate:
case ss.linkedConn != nil: return liberrors.ErrServerTerminated{}
// otherwise, timeout happens when no requests arrives
default:
now := time.Now()
if now.Sub(ss.lastRequestTime) >= serverSessionCloseAfterNoRequestsFor {
break outer
}
} }
case <-receiverReportTicker.C:
if ss.state != ServerSessionStateRecord {
continue
}
now := time.Now()
for trackID, track := range ss.announcedTracks {
r := track.rtcpReceiver.Report(now)
ss.WriteFrame(trackID, StreamTypeRTCP, r)
}
case <-ss.terminate:
break outer
} }
} }()
go func() { go func() {
for req := range ss.request { for req := range ss.request {
@@ -298,7 +299,7 @@ outer:
close(ss.request) close(ss.request)
if h, ok := ss.s.Handler.(ServerHandlerOnSessionClose); ok { if h, ok := ss.s.Handler.(ServerHandlerOnSessionClose); ok {
h.OnSessionClose(ss) h.OnSessionClose(ss, err)
} }
} }
@@ -758,7 +759,7 @@ func (ss *ServerSession) handleRequest(sc *ServerConn, req *base.Request) (*base
return &base.Response{ return &base.Response{
StatusCode: base.StatusOK, StatusCode: base.StatusOK,
}, liberrors.ErrServerTeardown{} }, liberrors.ErrServerSessionTeardown{}
case base.GetParameter: case base.GetParameter:
if h, ok := sc.s.Handler.(ServerHandlerOnGetParameter); ok { if h, ok := sc.s.Handler.(ServerHandlerOnGetParameter); ok {