move headers into dedicated folder

This commit is contained in:
aler9
2020-10-04 18:08:38 +02:00
parent 7ad47b8b30
commit a465335210
12 changed files with 170 additions and 141 deletions

11
auth.go
View File

@@ -10,14 +10,3 @@ func md5Hex(in string) string {
h.Write([]byte(in)) h.Write([]byte(in))
return hex.EncodeToString(h.Sum(nil)) return hex.EncodeToString(h.Sum(nil))
} }
// AuthMethod is an authentication method.
type AuthMethod int
const (
// Basic authentication method
Basic AuthMethod = iota
// Digest authentication method
Digest
)

View File

@@ -7,23 +7,24 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
"github.com/aler9/gortsplib/headers"
) )
var casesAuth = []struct { var casesAuth = []struct {
name string name string
methods []AuthMethod methods []headers.AuthMethod
}{ }{
{ {
"basic", "basic",
[]AuthMethod{Basic}, []headers.AuthMethod{headers.AuthBasic},
}, },
{ {
"digest", "digest",
[]AuthMethod{Digest}, []headers.AuthMethod{headers.AuthDigest},
}, },
{ {
"both", "both",
[]AuthMethod{Basic, Digest}, []headers.AuthMethod{headers.AuthBasic, headers.AuthDigest},
}, },
} }
@@ -46,7 +47,8 @@ func TestAuthMethods(t *testing.T) {
} }
func TestAuthBasePath(t *testing.T) { func TestAuthBasePath(t *testing.T) {
authServer := NewAuthServer("testuser", "testpass", []AuthMethod{Basic, Digest}) authServer := NewAuthServer("testuser", "testpass",
[]headers.AuthMethod{headers.AuthBasic, headers.AuthDigest})
wwwAuthenticate := authServer.GenerateHeader() wwwAuthenticate := authServer.GenerateHeader()
ac, err := newAuthClient(wwwAuthenticate, "testuser", "testpass") ac, err := newAuthClient(wwwAuthenticate, "testuser", "testpass")

View File

@@ -7,6 +7,7 @@ import (
"strings" "strings"
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
"github.com/aler9/gortsplib/headers"
) )
// authClient is an object that helps a client to send its credentials to a // authClient is an object that helps a client to send its credentials to a
@@ -14,7 +15,7 @@ import (
type authClient struct { type authClient struct {
user string user string
pass string pass string
method AuthMethod method headers.AuthMethod
realm string realm string
nonce string nonce string
} }
@@ -31,7 +32,7 @@ func newAuthClient(v base.HeaderValue, user string, pass string) (*authClient, e
} }
return "" return ""
}(); headerAuthDigest != "" { }(); headerAuthDigest != "" {
auth, err := ReadHeaderAuth(base.HeaderValue{headerAuthDigest}) auth, err := headers.ReadAuth(base.HeaderValue{headerAuthDigest})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -47,7 +48,7 @@ func newAuthClient(v base.HeaderValue, user string, pass string) (*authClient, e
return &authClient{ return &authClient{
user: user, user: user,
pass: pass, pass: pass,
method: Digest, method: headers.AuthDigest,
realm: *auth.Realm, realm: *auth.Realm,
nonce: *auth.Nonce, nonce: *auth.Nonce,
}, nil }, nil
@@ -61,7 +62,7 @@ func newAuthClient(v base.HeaderValue, user string, pass string) (*authClient, e
} }
return "" return ""
}(); headerAuthBasic != "" { }(); headerAuthBasic != "" {
auth, err := ReadHeaderAuth(base.HeaderValue{headerAuthBasic}) auth, err := headers.ReadAuth(base.HeaderValue{headerAuthBasic})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -73,7 +74,7 @@ func newAuthClient(v base.HeaderValue, user string, pass string) (*authClient, e
return &authClient{ return &authClient{
user: user, user: user,
pass: pass, pass: pass,
method: Basic, method: headers.AuthBasic,
realm: *auth.Realm, realm: *auth.Realm,
}, nil }, nil
} }
@@ -85,17 +86,17 @@ func newAuthClient(v base.HeaderValue, user string, pass string) (*authClient, e
// the given method and url. // the given method and url.
func (ac *authClient) GenerateHeader(method base.Method, ur *url.URL) base.HeaderValue { func (ac *authClient) GenerateHeader(method base.Method, ur *url.URL) base.HeaderValue {
switch ac.method { switch ac.method {
case Basic: case headers.AuthBasic:
response := base64.StdEncoding.EncodeToString([]byte(ac.user + ":" + ac.pass)) response := base64.StdEncoding.EncodeToString([]byte(ac.user + ":" + ac.pass))
return base.HeaderValue{"Basic " + response} return base.HeaderValue{"Basic " + response}
case Digest: case headers.AuthDigest:
response := md5Hex(md5Hex(ac.user+":"+ac.realm+":"+ac.pass) + ":" + response := md5Hex(md5Hex(ac.user+":"+ac.realm+":"+ac.pass) + ":" +
ac.nonce + ":" + md5Hex(string(method)+":"+ur.String())) ac.nonce + ":" + md5Hex(string(method)+":"+ur.String()))
return (&HeaderAuth{ return (&headers.Auth{
Method: Digest, Method: headers.AuthDigest,
Username: &ac.user, Username: &ac.user,
Realm: &ac.realm, Realm: &ac.realm,
Nonce: &ac.nonce, Nonce: &ac.nonce,

View File

@@ -9,6 +9,7 @@ import (
"strings" "strings"
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
"github.com/aler9/gortsplib/headers"
) )
// AuthServer is an object that helps a server to validate the credentials of // AuthServer is an object that helps a server to validate the credentials of
@@ -16,16 +17,16 @@ import (
type AuthServer struct { type AuthServer struct {
user string user string
pass string pass string
methods []AuthMethod methods []headers.AuthMethod
realm string realm string
nonce string nonce string
} }
// NewAuthServer allocates an AuthServer. // NewAuthServer allocates an AuthServer.
// If methods is nil, the Basic and Digest methods are used. // If methods is nil, the Basic and Digest methods are used.
func NewAuthServer(user string, pass string, methods []AuthMethod) *AuthServer { func NewAuthServer(user string, pass string, methods []headers.AuthMethod) *AuthServer {
if methods == nil { if methods == nil {
methods = []AuthMethod{Basic, Digest} methods = []headers.AuthMethod{headers.AuthBasic, headers.AuthDigest}
} }
nonceByts := make([]byte, 16) nonceByts := make([]byte, 16)
@@ -46,15 +47,15 @@ func (as *AuthServer) GenerateHeader() base.HeaderValue {
var ret base.HeaderValue var ret base.HeaderValue
for _, m := range as.methods { for _, m := range as.methods {
switch m { switch m {
case Basic: case headers.AuthBasic:
ret = append(ret, (&HeaderAuth{ ret = append(ret, (&headers.Auth{
Method: Basic, Method: headers.AuthBasic,
Realm: &as.realm, Realm: &as.realm,
}).Write()...) }).Write()...)
case Digest: case headers.AuthDigest:
ret = append(ret, (&HeaderAuth{ ret = append(ret, (&headers.Auth{
Method: Digest, Method: headers.AuthDigest,
Realm: &as.realm, Realm: &as.realm,
Nonce: &as.nonce, Nonce: &as.nonce,
}).Write()...) }).Write()...)
@@ -85,7 +86,7 @@ func (as *AuthServer) ValidateHeader(v base.HeaderValue, method base.Method, ur
} }
} else if strings.HasPrefix(v0, "Digest ") { } else if strings.HasPrefix(v0, "Digest ") {
auth, err := ReadHeaderAuth(base.HeaderValue{v0}) auth, err := headers.ReadAuth(base.HeaderValue{v0})
if err != nil { if err != nil {
return err return err
} }

View File

@@ -19,6 +19,7 @@ import (
"time" "time"
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
"github.com/aler9/gortsplib/headers"
"github.com/aler9/gortsplib/rtcpreceiver" "github.com/aler9/gortsplib/rtcpreceiver"
) )
@@ -297,7 +298,7 @@ func (c *ConnClient) Do(req *base.Request) (*base.Response, error) {
// get session from response // get session from response
if v, ok := res.Header["Session"]; ok { if v, ok := res.Header["Session"]; ok {
sx, err := ReadHeaderSession(v) sx, err := headers.ReadSession(v)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse session header: %s", err) return nil, fmt.Errorf("unable to parse session header: %s", err)
} }
@@ -448,7 +449,7 @@ func (c *ConnClient) urlForTrack(baseUrl *url.URL, mode SetupMode, track *Track)
return u return u
} }
func (c *ConnClient) setup(u *url.URL, mode SetupMode, track *Track, ht *HeaderTransport) (*base.Response, error) { func (c *ConnClient) setup(u *url.URL, mode SetupMode, track *Track, ht *headers.Transport) (*base.Response, error) {
res, err := c.Do(&base.Request{ res, err := c.Do(&base.Request{
Method: base.SETUP, Method: base.SETUP,
Url: c.urlForTrack(u, mode, track), Url: c.urlForTrack(u, mode, track),
@@ -533,7 +534,7 @@ func (c *ConnClient) SetupUDP(u *url.URL, mode SetupMode, track *Track, rtpPort
return nil, err return nil, err
} }
res, err := c.setup(u, mode, track, &HeaderTransport{ res, err := c.setup(u, mode, track, &headers.Transport{
Protocol: StreamProtocolUDP, Protocol: StreamProtocolUDP,
Cast: func() *StreamCast { Cast: func() *StreamCast {
ret := StreamUnicast ret := StreamUnicast
@@ -556,7 +557,7 @@ func (c *ConnClient) SetupUDP(u *url.URL, mode SetupMode, track *Track, rtpPort
return nil, err return nil, err
} }
th, err := ReadHeaderTransport(res.Header["Transport"]) th, err := headers.ReadTransport(res.Header["Transport"])
if err != nil { if err != nil {
rtpListener.close() rtpListener.close()
rtcpListener.close() rtcpListener.close()
@@ -608,7 +609,7 @@ func (c *ConnClient) SetupTCP(u *url.URL, mode SetupMode, track *Track) (*base.R
} }
interleavedIds := [2]int{(track.Id * 2), (track.Id * 2) + 1} interleavedIds := [2]int{(track.Id * 2), (track.Id * 2) + 1}
res, err := c.setup(u, mode, track, &HeaderTransport{ res, err := c.setup(u, mode, track, &headers.Transport{
Protocol: StreamProtocolTCP, Protocol: StreamProtocolTCP,
Cast: func() *StreamCast { Cast: func() *StreamCast {
ret := StreamUnicast ret := StreamUnicast
@@ -629,7 +630,7 @@ func (c *ConnClient) SetupTCP(u *url.URL, mode SetupMode, track *Track) (*base.R
return nil, err return nil, err
} }
th, err := ReadHeaderTransport(res.Header["Transport"]) th, err := headers.ReadTransport(res.Header["Transport"])
if err != nil { if err != nil {
return nil, fmt.Errorf("transport header: %s", err) return nil, fmt.Errorf("transport header: %s", err)
} }

View File

@@ -1,4 +1,5 @@
package gortsplib // Package headers contains various RTSP headers.
package headers
import ( import (
"fmt" "fmt"
@@ -7,8 +8,19 @@ import (
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
) )
// HeaderAuth is an Authenticate or a WWWW-Authenticate header. // AuthMethod is an authentication method.
type HeaderAuth struct { type AuthMethod int
const (
// AuthBasic is the Basic authentication method
AuthBasic AuthMethod = iota
// AuthDigest is the Digest authentication method
AuthDigest
)
// Auth is an Authenticate or a WWWW-Authenticate header.
type Auth struct {
// authentication method // authentication method
Method AuthMethod Method AuthMethod
@@ -67,8 +79,8 @@ func findValue(v0 string) (string, string, error) {
} }
} }
// ReadHeaderAuth parses an Authenticate or a WWW-Authenticate header. // ReadAuth parses an Authenticate or a WWW-Authenticate header.
func ReadHeaderAuth(v base.HeaderValue) (*HeaderAuth, error) { func ReadAuth(v base.HeaderValue) (*Auth, error) {
if len(v) == 0 { if len(v) == 0 {
return nil, fmt.Errorf("value not provided") return nil, fmt.Errorf("value not provided")
} }
@@ -77,7 +89,7 @@ func ReadHeaderAuth(v base.HeaderValue) (*HeaderAuth, error) {
return nil, fmt.Errorf("value provided multiple times (%v)", v) return nil, fmt.Errorf("value provided multiple times (%v)", v)
} }
ha := &HeaderAuth{} ha := &Auth{}
v0 := v[0] v0 := v[0]
@@ -88,10 +100,10 @@ func ReadHeaderAuth(v base.HeaderValue) (*HeaderAuth, error) {
switch v0[:i] { switch v0[:i] {
case "Basic": case "Basic":
ha.Method = Basic ha.Method = AuthBasic
case "Digest": case "Digest":
ha.Method = Digest ha.Method = AuthDigest
default: default:
return nil, fmt.Errorf("invalid method (%s)", v0[:i]) return nil, fmt.Errorf("invalid method (%s)", v0[:i])
@@ -156,14 +168,14 @@ func ReadHeaderAuth(v base.HeaderValue) (*HeaderAuth, error) {
} }
// Write encodes an Authenticate or a WWW-Authenticate header. // Write encodes an Authenticate or a WWW-Authenticate header.
func (ha *HeaderAuth) Write() base.HeaderValue { func (ha *Auth) Write() base.HeaderValue {
ret := "" ret := ""
switch ha.Method { switch ha.Method {
case Basic: case AuthBasic:
ret += "Basic" ret += "Basic"
case Digest: case AuthDigest:
ret += "Digest" ret += "Digest"
} }

View File

@@ -1,4 +1,4 @@
package gortsplib package headers
import ( import (
"testing" "testing"
@@ -8,18 +8,18 @@ import (
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
) )
var casesHeaderAuth = []struct { var casesAuth = []struct {
name string name string
vin base.HeaderValue vin base.HeaderValue
vout base.HeaderValue vout base.HeaderValue
h *HeaderAuth h *Auth
}{ }{
{ {
"basic", "basic",
base.HeaderValue{`Basic realm="4419b63f5e51"`}, base.HeaderValue{`Basic realm="4419b63f5e51"`},
base.HeaderValue{`Basic realm="4419b63f5e51"`}, base.HeaderValue{`Basic realm="4419b63f5e51"`},
&HeaderAuth{ &Auth{
Method: Basic, Method: AuthBasic,
Realm: func() *string { Realm: func() *string {
v := "4419b63f5e51" v := "4419b63f5e51"
return &v return &v
@@ -30,8 +30,8 @@ var casesHeaderAuth = []struct {
"digest request 1", "digest request 1",
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`}, base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`}, base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
&HeaderAuth{ &Auth{
Method: Digest, Method: AuthDigest,
Realm: func() *string { Realm: func() *string {
v := "4419b63f5e51" v := "4419b63f5e51"
return &v return &v
@@ -50,8 +50,8 @@ var casesHeaderAuth = []struct {
"digest request 2", "digest request 2",
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale=FALSE`}, base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale=FALSE`},
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`}, base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
&HeaderAuth{ &Auth{
Method: Digest, Method: AuthDigest,
Realm: func() *string { Realm: func() *string {
v := "4419b63f5e51" v := "4419b63f5e51"
return &v return &v
@@ -70,8 +70,8 @@ var casesHeaderAuth = []struct {
"digest request 3", "digest request 3",
base.HeaderValue{`Digest realm="4419b63f5e51",nonce="133767111917411116111311118211673010032", stale="FALSE"`}, base.HeaderValue{`Digest realm="4419b63f5e51",nonce="133767111917411116111311118211673010032", stale="FALSE"`},
base.HeaderValue{`Digest realm="4419b63f5e51", nonce="133767111917411116111311118211673010032", stale="FALSE"`}, base.HeaderValue{`Digest realm="4419b63f5e51", nonce="133767111917411116111311118211673010032", stale="FALSE"`},
&HeaderAuth{ &Auth{
Method: Digest, Method: AuthDigest,
Realm: func() *string { Realm: func() *string {
v := "4419b63f5e51" v := "4419b63f5e51"
return &v return &v
@@ -90,8 +90,8 @@ var casesHeaderAuth = []struct {
"digest response generic", "digest response generic",
base.HeaderValue{`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`}, base.HeaderValue{`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`},
base.HeaderValue{`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`}, base.HeaderValue{`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`},
&HeaderAuth{ &Auth{
Method: Digest, Method: AuthDigest,
Username: func() *string { Username: func() *string {
v := "aa" v := "aa"
return &v return &v
@@ -118,8 +118,8 @@ var casesHeaderAuth = []struct {
"digest response with empty field", "digest response with empty field",
base.HeaderValue{`Digest username="", realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", response="c072ae90eb4a27f4cdcb90d62266b2a1"`}, base.HeaderValue{`Digest username="", realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", response="c072ae90eb4a27f4cdcb90d62266b2a1"`},
base.HeaderValue{`Digest username="", realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", response="c072ae90eb4a27f4cdcb90d62266b2a1"`}, base.HeaderValue{`Digest username="", realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", response="c072ae90eb4a27f4cdcb90d62266b2a1"`},
&HeaderAuth{ &Auth{
Method: Digest, Method: AuthDigest,
Username: func() *string { Username: func() *string {
v := "" v := ""
return &v return &v
@@ -146,8 +146,8 @@ var casesHeaderAuth = []struct {
"digest response with no spaces and additional fields", "digest response with no spaces and additional fields",
base.HeaderValue{`Digest realm="Please log in with a valid username",nonce="752a62306daf32b401a41004555c7663",opaque="",stale=FALSE,algorithm=MD5`}, base.HeaderValue{`Digest realm="Please log in with a valid username",nonce="752a62306daf32b401a41004555c7663",opaque="",stale=FALSE,algorithm=MD5`},
base.HeaderValue{`Digest realm="Please log in with a valid username", nonce="752a62306daf32b401a41004555c7663", opaque="", stale="FALSE", algorithm="MD5"`}, base.HeaderValue{`Digest realm="Please log in with a valid username", nonce="752a62306daf32b401a41004555c7663", opaque="", stale="FALSE", algorithm="MD5"`},
&HeaderAuth{ &Auth{
Method: Digest, Method: AuthDigest,
Realm: func() *string { Realm: func() *string {
v := "Please log in with a valid username" v := "Please log in with a valid username"
return &v return &v
@@ -172,18 +172,18 @@ var casesHeaderAuth = []struct {
}, },
} }
func TestHeaderAuthRead(t *testing.T) { func TestAuthRead(t *testing.T) {
for _, c := range casesHeaderAuth { for _, c := range casesAuth {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
req, err := ReadHeaderAuth(c.vin) req, err := ReadAuth(c.vin)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, c.h, req) require.Equal(t, c.h, req)
}) })
} }
} }
func TestHeaderAuthWrite(t *testing.T) { func TestAuthWrite(t *testing.T) {
for _, c := range casesHeaderAuth { for _, c := range casesAuth {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
req := c.h.Write() req := c.h.Write()
require.Equal(t, c.vout, req) require.Equal(t, c.vout, req)

View File

@@ -1,4 +1,4 @@
package gortsplib package headers
import ( import (
"fmt" "fmt"
@@ -8,8 +8,8 @@ import (
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
) )
// HeaderSession is a Session header. // Session is a Session header.
type HeaderSession struct { type Session struct {
// session id // session id
Session string Session string
@@ -17,8 +17,8 @@ type HeaderSession struct {
Timeout *uint Timeout *uint
} }
// ReadHeaderSession parses a Session header. // ReadSession parses a Session header.
func ReadHeaderSession(v base.HeaderValue) (*HeaderSession, error) { func ReadSession(v base.HeaderValue) (*Session, error) {
if len(v) == 0 { if len(v) == 0 {
return nil, fmt.Errorf("value not provided") return nil, fmt.Errorf("value not provided")
} }
@@ -32,7 +32,7 @@ func ReadHeaderSession(v base.HeaderValue) (*HeaderSession, error) {
return nil, fmt.Errorf("invalid value (%v)", v) return nil, fmt.Errorf("invalid value (%v)", v)
} }
hs := &HeaderSession{} hs := &Session{}
hs.Session = parts[0] hs.Session = parts[0]
@@ -63,7 +63,7 @@ func ReadHeaderSession(v base.HeaderValue) (*HeaderSession, error) {
} }
// Write encodes a Session header // Write encodes a Session header
func (hs *HeaderSession) Write() base.HeaderValue { func (hs *Session) Write() base.HeaderValue {
val := hs.Session val := hs.Session
if hs.Timeout != nil { if hs.Timeout != nil {

View File

@@ -1,4 +1,4 @@
package gortsplib package headers
import ( import (
"testing" "testing"
@@ -8,17 +8,17 @@ import (
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
) )
var casesHeaderSession = []struct { var casesSession = []struct {
name string name string
vin base.HeaderValue vin base.HeaderValue
vout base.HeaderValue vout base.HeaderValue
h *HeaderSession h *Session
}{ }{
{ {
"base", "base",
base.HeaderValue{`A3eqwsafq3rFASqew`}, base.HeaderValue{`A3eqwsafq3rFASqew`},
base.HeaderValue{`A3eqwsafq3rFASqew`}, base.HeaderValue{`A3eqwsafq3rFASqew`},
&HeaderSession{ &Session{
Session: "A3eqwsafq3rFASqew", Session: "A3eqwsafq3rFASqew",
}, },
}, },
@@ -26,7 +26,7 @@ var casesHeaderSession = []struct {
"with timeout", "with timeout",
base.HeaderValue{`A3eqwsafq3rFASqew;timeout=47`}, base.HeaderValue{`A3eqwsafq3rFASqew;timeout=47`},
base.HeaderValue{`A3eqwsafq3rFASqew;timeout=47`}, base.HeaderValue{`A3eqwsafq3rFASqew;timeout=47`},
&HeaderSession{ &Session{
Session: "A3eqwsafq3rFASqew", Session: "A3eqwsafq3rFASqew",
Timeout: func() *uint { Timeout: func() *uint {
v := uint(47) v := uint(47)
@@ -38,7 +38,7 @@ var casesHeaderSession = []struct {
"with timeout and space", "with timeout and space",
base.HeaderValue{`A3eqwsafq3rFASqew; timeout=47`}, base.HeaderValue{`A3eqwsafq3rFASqew; timeout=47`},
base.HeaderValue{`A3eqwsafq3rFASqew;timeout=47`}, base.HeaderValue{`A3eqwsafq3rFASqew;timeout=47`},
&HeaderSession{ &Session{
Session: "A3eqwsafq3rFASqew", Session: "A3eqwsafq3rFASqew",
Timeout: func() *uint { Timeout: func() *uint {
v := uint(47) v := uint(47)
@@ -48,18 +48,18 @@ var casesHeaderSession = []struct {
}, },
} }
func TestHeaderSessionRead(t *testing.T) { func TestSessionRead(t *testing.T) {
for _, c := range casesHeaderSession { for _, c := range casesSession {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
req, err := ReadHeaderSession(c.vin) req, err := ReadSession(c.vin)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, c.h, req) require.Equal(t, c.h, req)
}) })
} }
} }
func TestHeaderSessionWrite(t *testing.T) { func TestSessionWrite(t *testing.T) {
for _, c := range casesHeaderSession { for _, c := range casesSession {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
req := c.h.Write() req := c.h.Write()
require.Equal(t, c.vout, req) require.Equal(t, c.vout, req)

View File

@@ -1,4 +1,4 @@
package gortsplib package headers
import ( import (
"fmt" "fmt"
@@ -8,8 +8,54 @@ import (
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
) )
// HeaderTransport is a Transport header. // StreamProtocol is the protocol of a stream.
type HeaderTransport struct { type StreamProtocol int
const (
// StreamProtocolUDP means that the stream uses the UDP protocol
StreamProtocolUDP StreamProtocol = iota
// StreamProtocolTCP means that the stream uses the TCP protocol
StreamProtocolTCP
)
// String implements fmt.Stringer.
func (sp StreamProtocol) String() string {
switch sp {
case StreamProtocolUDP:
return "udp"
case StreamProtocolTCP:
return "tcp"
}
return "unknown"
}
// StreamCast is the cast method of a stream.
type StreamCast int
const (
// StreamUnicast means that the stream is unicasted
StreamUnicast StreamCast = iota
// StreamMulticast means that the stream is multicasted
StreamMulticast
)
// String implements fmt.Stringer.
func (sc StreamCast) String() string {
switch sc {
case StreamUnicast:
return "unicast"
case StreamMulticast:
return "multicast"
}
return "unknown"
}
// Transport is a Transport header.
type Transport struct {
// protocol of the stream // protocol of the stream
Protocol StreamProtocol Protocol StreamProtocol
@@ -57,8 +103,8 @@ func parsePorts(val string) (*[2]int, error) {
return &[2]int{int(port1), int(port2)}, nil return &[2]int{int(port1), int(port2)}, nil
} }
// ReadHeaderTransport parses a Transport header. // ReadTransport parses a Transport header.
func ReadHeaderTransport(v base.HeaderValue) (*HeaderTransport, error) { func ReadTransport(v base.HeaderValue) (*Transport, error) {
if len(v) == 0 { if len(v) == 0 {
return nil, fmt.Errorf("value not provided") return nil, fmt.Errorf("value not provided")
} }
@@ -67,7 +113,7 @@ func ReadHeaderTransport(v base.HeaderValue) (*HeaderTransport, error) {
return nil, fmt.Errorf("value provided multiple times (%v)", v) return nil, fmt.Errorf("value provided multiple times (%v)", v)
} }
ht := &HeaderTransport{} ht := &Transport{}
parts := strings.Split(v[0], ";") parts := strings.Split(v[0], ";")
if len(parts) == 0 { if len(parts) == 0 {
@@ -153,7 +199,7 @@ func ReadHeaderTransport(v base.HeaderValue) (*HeaderTransport, error) {
} }
// Write encodes a Transport header // Write encodes a Transport header
func (ht *HeaderTransport) Write() base.HeaderValue { func (ht *Transport) Write() base.HeaderValue {
var vals []string var vals []string
if ht.Protocol == StreamProtocolUDP { if ht.Protocol == StreamProtocolUDP {

View File

@@ -1,4 +1,4 @@
package gortsplib package headers
import ( import (
"testing" "testing"
@@ -8,17 +8,17 @@ import (
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
) )
var casesHeaderTransport = []struct { var casesTransport = []struct {
name string name string
vin base.HeaderValue vin base.HeaderValue
vout base.HeaderValue vout base.HeaderValue
h *HeaderTransport h *Transport
}{ }{
{ {
"udp unicast play request", "udp unicast play request",
base.HeaderValue{`RTP/AVP;unicast;client_port=3456-3457;mode="PLAY"`}, base.HeaderValue{`RTP/AVP;unicast;client_port=3456-3457;mode="PLAY"`},
base.HeaderValue{`RTP/AVP;unicast;client_port=3456-3457;mode=play`}, base.HeaderValue{`RTP/AVP;unicast;client_port=3456-3457;mode=play`},
&HeaderTransport{ &Transport{
Protocol: StreamProtocolUDP, Protocol: StreamProtocolUDP,
Cast: func() *StreamCast { Cast: func() *StreamCast {
v := StreamUnicast v := StreamUnicast
@@ -35,7 +35,7 @@ var casesHeaderTransport = []struct {
"udp unicast play response", "udp unicast play response",
base.HeaderValue{`RTP/AVP/UDP;unicast;client_port=3056-3057;server_port=5000-5001`}, base.HeaderValue{`RTP/AVP/UDP;unicast;client_port=3056-3057;server_port=5000-5001`},
base.HeaderValue{`RTP/AVP;unicast;client_port=3056-3057;server_port=5000-5001`}, base.HeaderValue{`RTP/AVP;unicast;client_port=3056-3057;server_port=5000-5001`},
&HeaderTransport{ &Transport{
Protocol: StreamProtocolUDP, Protocol: StreamProtocolUDP,
Cast: func() *StreamCast { Cast: func() *StreamCast {
v := StreamUnicast v := StreamUnicast
@@ -49,7 +49,7 @@ var casesHeaderTransport = []struct {
"udp multicast play request / response", "udp multicast play request / response",
base.HeaderValue{`RTP/AVP;multicast;destination=225.219.201.15;port=7000-7001;ttl=127`}, base.HeaderValue{`RTP/AVP;multicast;destination=225.219.201.15;port=7000-7001;ttl=127`},
base.HeaderValue{`RTP/AVP;multicast`}, base.HeaderValue{`RTP/AVP;multicast`},
&HeaderTransport{ &Transport{
Protocol: StreamProtocolUDP, Protocol: StreamProtocolUDP,
Cast: func() *StreamCast { Cast: func() *StreamCast {
v := StreamMulticast v := StreamMulticast
@@ -70,25 +70,25 @@ var casesHeaderTransport = []struct {
"tcp play request / response", "tcp play request / response",
base.HeaderValue{`RTP/AVP/TCP;interleaved=0-1`}, base.HeaderValue{`RTP/AVP/TCP;interleaved=0-1`},
base.HeaderValue{`RTP/AVP/TCP;interleaved=0-1`}, base.HeaderValue{`RTP/AVP/TCP;interleaved=0-1`},
&HeaderTransport{ &Transport{
Protocol: StreamProtocolTCP, Protocol: StreamProtocolTCP,
InterleavedIds: &[2]int{0, 1}, InterleavedIds: &[2]int{0, 1},
}, },
}, },
} }
func TestHeaderTransportRead(t *testing.T) { func TestTransportRead(t *testing.T) {
for _, c := range casesHeaderTransport { for _, c := range casesTransport {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
req, err := ReadHeaderTransport(c.vin) req, err := ReadTransport(c.vin)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, c.h, req) require.Equal(t, c.h, req)
}) })
} }
} }
func TestHeaderTransportWrite(t *testing.T) { func TestTransportWrite(t *testing.T) {
for _, c := range casesHeaderTransport { for _, c := range casesTransport {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
req := c.h.Write() req := c.h.Write()
require.Equal(t, c.vout, req) require.Equal(t, c.vout, req)

View File

@@ -2,54 +2,31 @@ package gortsplib
import ( import (
"github.com/aler9/gortsplib/base" "github.com/aler9/gortsplib/base"
"github.com/aler9/gortsplib/headers"
) )
// StreamProtocol is the protocol of a stream. // StreamProtocol is the protocol of a stream.
type StreamProtocol int type StreamProtocol = headers.StreamProtocol
const ( const (
// StreamProtocolUDP means that the stream uses the UDP protocol // StreamProtocolUDP means that the stream uses the UDP protocol
StreamProtocolUDP StreamProtocol = iota StreamProtocolUDP = headers.StreamProtocolUDP
// StreamProtocolTCP means that the stream uses the TCP protocol // StreamProtocolTCP means that the stream uses the TCP protocol
StreamProtocolTCP StreamProtocolTCP = headers.StreamProtocolTCP
) )
// String implements fmt.Stringer.
func (sp StreamProtocol) String() string {
switch sp {
case StreamProtocolUDP:
return "udp"
case StreamProtocolTCP:
return "tcp"
}
return "unknown"
}
// StreamCast is the cast method of a stream. // StreamCast is the cast method of a stream.
type StreamCast int type StreamCast = headers.StreamCast
const ( const (
// StreamUnicast means that the stream is unicasted // StreamUnicast means that the stream is unicasted
StreamUnicast StreamCast = iota StreamUnicast = headers.StreamUnicast
// StreamMulticast means that the stream is multicasted // StreamMulticast means that the stream is multicasted
StreamMulticast StreamMulticast = headers.StreamMulticast
) )
// String implements fmt.Stringer.
func (sc StreamCast) String() string {
switch sc {
case StreamUnicast:
return "unicast"
case StreamMulticast:
return "multicast"
}
return "unknown"
}
// StreamType is the stream type. // StreamType is the stream type.
type StreamType = base.StreamType type StreamType = base.StreamType