export most client errors

This commit is contained in:
aler9
2021-03-21 10:57:48 +01:00
parent c4d7552986
commit c0e045071b
4 changed files with 174 additions and 44 deletions

View File

@@ -35,6 +35,142 @@ const (
clientConnUDPKeepalivePeriod = 30 * time.Second clientConnUDPKeepalivePeriod = 30 * time.Second
) )
// ErrClientWrongState is returned in case of a wrong client state.
type ErrClientWrongState struct {
allowedList []clientConnState
state clientConnState
}
// Error implements the error interface.
func (e ErrClientWrongState) Error() string {
return fmt.Sprintf("must be in state %v, while is in state %v",
e.allowedList, e.state)
}
// ErrClientSessionHeaderInvalid is returned in case of an invalid session header.
type ErrClientSessionHeaderInvalid struct {
err error
}
// Error implements the error interface.
func (e ErrClientSessionHeaderInvalid) Error() string {
return fmt.Sprintf("invalid session header: %v", e.err)
}
// ErrClientWrongStatusCode is returned in case of a wrong status code.
type ErrClientWrongStatusCode struct {
code base.StatusCode
message string
}
// Error implements the error interface.
func (e ErrClientWrongStatusCode) Error() string {
return fmt.Sprintf("wrong status code: %d (%s)", e.code, e.message)
}
// ErrClientContentTypeMissing is returned in case the Content-Type header is missing.
type ErrClientContentTypeMissing struct{}
// Error implements the error interface.
func (e ErrClientContentTypeMissing) Error() string {
return "Content-Type header is missing"
}
// ErrClientContentTypeUnsupported is returned in case the Content-Type header is unsupported.
type ErrClientContentTypeUnsupported struct {
ct base.HeaderValue
}
// Error implements the error interface.
func (e ErrClientContentTypeUnsupported) Error() string {
return fmt.Sprintf("unsupported Content-Type header '%v'", e.ct)
}
// ErrClientCannotReadPublishAtSameTime is returned when the client is trying to read and publish at the same time.
type ErrClientCannotReadPublishAtSameTime struct{}
// Error implements the error interface.
func (e ErrClientCannotReadPublishAtSameTime) Error() string {
return "cannot read and publish at the same time"
}
// ErrClientCannotSetupTracksDifferentURLs is returned when the client is trying to setup tracks with different base URLs.
type ErrClientCannotSetupTracksDifferentURLs struct{}
// Error implements the error interface.
func (e ErrClientCannotSetupTracksDifferentURLs) Error() string {
return "cannot setup tracks with different base URLs"
}
// ErrClientUDPPortsZero is returned when one of the UDP ports is zero.
type ErrClientUDPPortsZero struct{}
// Error implements the error interface.
func (e ErrClientUDPPortsZero) Error() string {
return "rtpPort and rtcpPort must be both zero or non-zero"
}
// ErrClientUDPPortsNotConsecutive is returned when the two UDP ports are not consecutive.
type ErrClientUDPPortsNotConsecutive struct{}
// Error implements the error interface.
func (e ErrClientUDPPortsNotConsecutive) Error() string {
return "rtcpPort must be rtpPort + 1"
}
// ErrClientServerPortsZero is returned when one of the server ports is zero.
type ErrClientServerPortsZero struct{}
// Error implements the error interface.
func (e ErrClientServerPortsZero) Error() string {
return "server ports must be both zero or both not zero"
}
// ErrClientServerPortsNotProvided is returned in case the server ports have not been provided.
type ErrClientServerPortsNotProvided struct{}
// Error implements the error interface.
func (e ErrClientServerPortsNotProvided) Error() string {
return "server ports have not been provided. Use AnyPortEnable to communicate with this server"
}
// ErrClientTransportHeaderNoInterleavedIDs is returned in case the transport header doesn't contain interleaved IDs.
type ErrClientTransportHeaderNoInterleavedIDs struct{}
// Error implements the error interface.
func (e ErrClientTransportHeaderNoInterleavedIDs) Error() string {
return "transport header does not contain interleaved IDs"
}
// ErrClientTransportHeaderWrongInterleavedIDs is returned in case the transport header contains wrong interleaved IDs.
type ErrClientTransportHeaderWrongInterleavedIDs struct {
expected [2]int
value [2]int
}
// Error implements the error interface.
func (e ErrClientTransportHeaderWrongInterleavedIDs) Error() string {
return fmt.Sprintf("wrong interleaved IDs, expected %v, got %v", e.expected, e.value)
}
// ErrClientNoUDPPacketsRecently is returned when no UDP packets have been received recently.
type ErrClientNoUDPPacketsRecently struct{}
// Error implements the error interface.
func (e ErrClientNoUDPPacketsRecently) Error() string {
return "no UDP packets received recently (maybe there's a firewall/NAT in between)"
}
// ErrClientRTPInfoInvalid is returned in case of an invalid RTP-Info.
type ErrClientRTPInfoInvalid struct {
err error
}
// Error implements the error interface.
func (e ErrClientRTPInfoInvalid) Error() string {
return fmt.Sprintf("unable to parse RTP-Info: %v", e.err)
}
type clientConnState int type clientConnState int
const ( const (
@@ -199,8 +335,7 @@ func (c *ClientConn) checkState(allowed map[clientConnState]struct{}) error {
allowedList[i] = a allowedList[i] = a
i++ i++
} }
return fmt.Errorf("must be in state %v, while is in state %v", return ErrClientWrongState{allowedList, c.state}
allowedList, c.state)
} }
// NetConn returns the underlying net.Conn. // NetConn returns the underlying net.Conn.
@@ -271,7 +406,7 @@ func (c *ClientConn) Do(req *base.Request) (*base.Response, error) {
var sx headers.Session var sx headers.Session
err := sx.Read(v) err := sx.Read(v)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse session header: %s", err) return nil, ErrClientSessionHeaderInvalid{err}
} }
c.session = sx.Session c.session = sx.Session
} }
@@ -319,7 +454,7 @@ func (c *ClientConn) Options(u *base.URL) (*base.Response, error) {
if res.StatusCode == base.StatusNotFound { if res.StatusCode == base.StatusNotFound {
return res, nil return res, nil
} }
return res, fmt.Errorf("bad status code: %d (%s)", res.StatusCode, res.StatusMessage) return res, ErrClientWrongStatusCode{res.StatusCode, res.StatusMessage}
} }
c.getParameterSupported = func() bool { c.getParameterSupported = func() bool {
@@ -389,16 +524,16 @@ func (c *ClientConn) Describe(u *base.URL) (Tracks, *base.Response, error) {
return c.Describe(u) return c.Describe(u)
} }
return nil, res, fmt.Errorf("bad status code: %d (%s)", res.StatusCode, res.StatusMessage) return nil, res, ErrClientWrongStatusCode{res.StatusCode, res.StatusMessage}
} }
payloadType, ok := res.Header["Content-Type"] ct, ok := res.Header["Content-Type"]
if !ok || len(payloadType) != 1 { if !ok || len(ct) != 1 {
return nil, nil, fmt.Errorf("Content-Type not provided") return nil, nil, ErrClientContentTypeMissing{}
} }
if payloadType[0] != "application/sdp" { if ct[0] != "application/sdp" {
return nil, nil, fmt.Errorf("wrong Content-Type, expected application/sdp") return nil, nil, ErrClientContentTypeUnsupported{ct}
} }
tracks, err := ReadTracks(res.Body, u) tracks, err := ReadTracks(res.Body, u)
@@ -423,17 +558,14 @@ func (c *ClientConn) Setup(mode headers.TransportMode, track *Track,
return nil, err return nil, err
} }
if mode == headers.TransportModeRecord && c.state != clientConnStatePreRecord { if (mode == headers.TransportModeRecord && c.state != clientConnStatePreRecord) ||
return nil, fmt.Errorf("cannot read and publish at the same time") (mode == headers.TransportModePlay && c.state != clientConnStatePrePlay &&
} c.state != clientConnStateInitial) {
return nil, ErrClientCannotReadPublishAtSameTime{}
if mode == headers.TransportModePlay && c.state != clientConnStatePrePlay &&
c.state != clientConnStateInitial {
return nil, fmt.Errorf("cannot read and publish at the same time")
} }
if c.streamURL != nil && *track.BaseURL != *c.streamURL { if c.streamURL != nil && *track.BaseURL != *c.streamURL {
return nil, fmt.Errorf("cannot setup tracks with different base urls") return nil, ErrClientCannotSetupTracksDifferentURLs{}
} }
var rtpListener *clientConnUDPListener var rtpListener *clientConnUDPListener
@@ -472,11 +604,11 @@ func (c *ClientConn) Setup(mode headers.TransportMode, track *Track,
if proto == base.StreamProtocolUDP { if proto == base.StreamProtocolUDP {
if (rtpPort == 0 && rtcpPort != 0) || if (rtpPort == 0 && rtcpPort != 0) ||
(rtpPort != 0 && rtcpPort == 0) { (rtpPort != 0 && rtcpPort == 0) {
return nil, fmt.Errorf("rtpPort and rtcpPort must be both zero or non-zero") return nil, ErrClientUDPPortsZero{}
} }
if rtpPort != 0 && rtcpPort != (rtpPort+1) { if rtpPort != 0 && rtcpPort != (rtpPort+1) {
return nil, fmt.Errorf("rtcpPort must be rtpPort + 1") return nil, ErrClientUDPPortsNotConsecutive{}
} }
var err error var err error
@@ -568,7 +700,7 @@ func (c *ClientConn) Setup(mode headers.TransportMode, track *Track,
return c.Setup(headers.TransportModePlay, track, 0, 0) return c.Setup(headers.TransportModePlay, track, 0, 0)
} }
return res, fmt.Errorf("bad status code: %d (%s)", res.StatusCode, res.StatusMessage) return res, ErrClientWrongStatusCode{res.StatusCode, res.StatusMessage}
} }
var thRes headers.Transport var thRes headers.Transport
@@ -587,29 +719,27 @@ func (c *ClientConn) Setup(mode headers.TransportMode, track *Track,
(thRes.ServerPorts[0] != 0 && thRes.ServerPorts[1] == 0) { (thRes.ServerPorts[0] != 0 && thRes.ServerPorts[1] == 0) {
rtpListener.close() rtpListener.close()
rtcpListener.close() rtcpListener.close()
return nil, fmt.Errorf("server ports must be both zero or both not zero") return nil, ErrClientServerPortsZero{}
} }
} }
if !c.conf.AnyPortEnable { if !c.conf.AnyPortEnable {
if thRes.ServerPorts == nil { if thRes.ServerPorts == nil || (thRes.ServerPorts[0] == 0 && thRes.ServerPorts[1] == 0) {
rtpListener.close() rtpListener.close()
rtcpListener.close() rtcpListener.close()
return nil, fmt.Errorf("server ports have not been provided. Use AnyPortEnable to communicate with this server") return nil, ErrClientServerPortsNotProvided{}
}
if thRes.ServerPorts[0] == 0 && thRes.ServerPorts[1] == 0 {
rtpListener.close()
rtcpListener.close()
return nil, fmt.Errorf("server ports have not been provided. Use AnyPortEnable to communicate with this server")
} }
} }
} else if thRes.InterleavedIDs == nil || } else {
thRes.InterleavedIDs[0] != th.InterleavedIDs[0] || if thRes.InterleavedIDs == nil {
return nil, ErrClientTransportHeaderNoInterleavedIDs{}
}
if thRes.InterleavedIDs[0] != th.InterleavedIDs[0] ||
thRes.InterleavedIDs[1] != th.InterleavedIDs[1] { thRes.InterleavedIDs[1] != th.InterleavedIDs[1] {
return nil, fmt.Errorf("transport header does not have interleaved ids %v (%s)", return nil, ErrClientTransportHeaderWrongInterleavedIDs{*th.InterleavedIDs, *thRes.InterleavedIDs}
*th.InterleavedIDs, res.Header["Transport"]) }
} }
clockRate, _ := track.ClockRate() clockRate, _ := track.ClockRate()
@@ -681,7 +811,7 @@ func (c *ClientConn) Pause() (*base.Response, error) {
} }
if res.StatusCode != base.StatusOK { if res.StatusCode != base.StatusOK {
return res, fmt.Errorf("bad status code: %d (%s)", res.StatusCode, res.StatusMessage) return res, ErrClientWrongStatusCode{res.StatusCode, res.StatusMessage}
} }
switch c.state { switch c.state {

View File

@@ -43,7 +43,7 @@ func (c *ClientConn) Announce(u *base.URL, tracks Tracks) (*base.Response, error
} }
if res.StatusCode != base.StatusOK { if res.StatusCode != base.StatusOK {
return nil, fmt.Errorf("bad status code: %d (%s)", res.StatusCode, res.StatusMessage) return nil, ErrClientWrongStatusCode{res.StatusCode, res.StatusMessage}
} }
c.streamURL = u c.streamURL = u
@@ -71,7 +71,7 @@ func (c *ClientConn) Record() (*base.Response, error) {
} }
if res.StatusCode != base.StatusOK { if res.StatusCode != base.StatusOK {
return nil, fmt.Errorf("bad status code: %d (%s)", res.StatusCode, res.StatusMessage) return nil, ErrClientWrongStatusCode{res.StatusCode, res.StatusMessage}
} }
c.state = clientConnStateRecord c.state = clientConnStateRecord

View File

@@ -28,14 +28,14 @@ func (c *ClientConn) Play() (*base.Response, error) {
} }
if res.StatusCode != base.StatusOK { if res.StatusCode != base.StatusOK {
return nil, fmt.Errorf("bad status code: %d (%s)", res.StatusCode, res.StatusMessage) return nil, ErrClientWrongStatusCode{res.StatusCode, res.StatusMessage}
} }
if v, ok := res.Header["RTP-Info"]; ok { if v, ok := res.Header["RTP-Info"]; ok {
var ri headers.RTPInfo var ri headers.RTPInfo
err := ri.Read(v) err := ri.Read(v)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse RTP-Info: %v", err) return nil, ErrClientRTPInfoInvalid{err}
} }
c.rtpInfo = &ri c.rtpInfo = &ri
} }
@@ -144,7 +144,7 @@ func (c *ClientConn) backgroundPlayUDP(done chan error) {
if now.Sub(last) >= c.conf.ReadTimeout { if now.Sub(last) >= c.conf.ReadTimeout {
c.nconn.SetReadDeadline(time.Now()) c.nconn.SetReadDeadline(time.Now())
<-readerDone <-readerDone
returnError = fmt.Errorf("no UDP packets received recently (maybe there's a firewall/NAT in between)") returnError = ErrClientNoUDPPacketsRecently{}
return return
} }
} }

View File

@@ -75,7 +75,7 @@ type ErrServerContentTypeUnsupported struct {
// Error implements the error interface. // Error implements the error interface.
func (e ErrServerContentTypeUnsupported) Error() string { func (e ErrServerContentTypeUnsupported) Error() string {
return fmt.Sprintf("unsupported Content-Type header '%s'", e.ct) return fmt.Sprintf("unsupported Content-Type header '%v'", e.ct)
} }
// ErrServerSDPInvalid is returned in case the SDP is invalid. // ErrServerSDPInvalid is returned in case the SDP is invalid.
@@ -139,7 +139,7 @@ type ErrServerTransportHeaderNoInterleavedIDs struct{}
// Error implements the error interface. // Error implements the error interface.
func (e ErrServerTransportHeaderNoInterleavedIDs) Error() string { func (e ErrServerTransportHeaderNoInterleavedIDs) Error() string {
return "transport header does not contain interleaved ids" return "transport header does not contain interleaved IDs"
} }
// ErrServerTransportHeaderWrongInterleavedIDs is returned in case the transport header contains wrong interleaved IDs. // ErrServerTransportHeaderWrongInterleavedIDs is returned in case the transport header contains wrong interleaved IDs.
@@ -150,7 +150,7 @@ type ErrServerTransportHeaderWrongInterleavedIDs struct {
// Error implements the error interface. // Error implements the error interface.
func (e ErrServerTransportHeaderWrongInterleavedIDs) Error() string { func (e ErrServerTransportHeaderWrongInterleavedIDs) Error() string {
return fmt.Sprintf("wrong interleaved ids, expected %v, got %v", e.expected, e.value) return fmt.Sprintf("wrong interleaved IDs, expected %v, got %v", e.expected, e.value)
} }
// ErrServerTracksDifferentProtocols is returned in case the client is trying to setup tracks with different protocols. // ErrServerTracksDifferentProtocols is returned in case the client is trying to setup tracks with different protocols.