add URL utils

This commit is contained in:
aler9
2020-10-31 14:56:31 +01:00
parent 75db154a17
commit d85cfae2ed
5 changed files with 206 additions and 28 deletions

View File

@@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/require"
)
func mustParseUrl(s string) *url.URL {
func urlMustParse(s string) *url.URL {
u, err := url.Parse(s)
if err != nil {
panic(err)
@@ -31,7 +31,7 @@ var casesRequest = []struct {
"\r\n"),
Request{
Method: "OPTIONS",
Url: mustParseUrl("rtsp://example.com/media.mp4"),
Url: urlMustParse("rtsp://example.com/media.mp4"),
Header: Header{
"CSeq": HeaderValue{"1"},
"Require": HeaderValue{"implicit-play"},
@@ -47,7 +47,7 @@ var casesRequest = []struct {
"\r\n"),
Request{
Method: "DESCRIBE",
Url: mustParseUrl("rtsp://example.com/media.mp4"),
Url: urlMustParse("rtsp://example.com/media.mp4"),
Header: Header{
"Accept": HeaderValue{"application/sdp"},
"CSeq": HeaderValue{"2"},
@@ -55,14 +55,14 @@ var casesRequest = []struct {
},
},
{
"describe with exclamation mark",
"describe with special chars",
[]byte("DESCRIBE rtsp://192.168.1.99:554/user=tmp&password=BagRep1!&channel=1&stream=0.sdp RTSP/1.0\r\n" +
"Accept: application/sdp\r\n" +
"CSeq: 3\r\n" +
"\r\n"),
Request{
Method: "DESCRIBE",
Url: mustParseUrl("rtsp://192.168.1.99:554/user=tmp&password=BagRep1!&channel=1&stream=0.sdp"),
Url: urlMustParse("rtsp://192.168.1.99:554/user=tmp&password=BagRep1!&channel=1&stream=0.sdp"),
Header: Header{
"Accept": HeaderValue{"application/sdp"},
"CSeq": HeaderValue{"3"},
@@ -91,7 +91,7 @@ var casesRequest = []struct {
"m=video 2232 RTP/AVP 31\n"),
Request{
Method: "ANNOUNCE",
Url: mustParseUrl("rtsp://example.com/media.mp4"),
Url: urlMustParse("rtsp://example.com/media.mp4"),
Header: Header{
"CSeq": HeaderValue{"7"},
"Date": HeaderValue{"23 Jan 1997 15:35:06 GMT"},
@@ -125,7 +125,7 @@ var casesRequest = []struct {
"jitter\n"),
Request{
Method: "GET_PARAMETER",
Url: mustParseUrl("rtsp://example.com/media.mp4"),
Url: urlMustParse("rtsp://example.com/media.mp4"),
Header: Header{
"CSeq": HeaderValue{"9"},
"Content-Type": HeaderValue{"text/parameters"},

88
base/url.go Normal file
View File

@@ -0,0 +1,88 @@
package base
import (
"strings"
"net/url"
)
func stringsReverseIndexByte(s string, c byte) int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == c {
return i
}
}
return -1
}
// URLGetBasePath returns the base path of a RTSP URL.
// We assume that the URL doesn't contain a control path.
func URLGetBasePath(u *url.URL) string {
if u.RawPath != "" {
return u.RawPath[1:] // remove leading slash
}
return u.Path[1:] // remove leading slash
}
// URLGetBaseControlPath returns the base path and the control path of a RTSP URL.
// We assume that the URL contains a control path.
func URLGetBaseControlPath(u *url.URL) (string, string, bool) {
pathAndQuery := ""
if u.RawPath != "" {
pathAndQuery += u.RawPath
} else {
pathAndQuery += u.Path
}
if u.RawQuery != "" {
pathAndQuery += "?" + u.RawQuery
}
pathAndQuery = pathAndQuery[1:] // remove leading slash
pos := stringsReverseIndexByte(pathAndQuery, '/')
if pos < 0 {
return "", "", false
}
basePath := pathAndQuery[:pos]
// remove query from basePath
i := strings.IndexByte(basePath, '?')
if i >= 0 {
basePath = basePath[:i]
}
if len(basePath) == 0 {
return "", "", false
}
controlPath := pathAndQuery[pos+1:]
if len(controlPath) == 0 {
return "", "", false
}
return basePath, controlPath, true
}
// URLAddControlPath adds a control path to a RTSP url.
func URLAddControlPath(u *url.URL, controlPath string) {
// always insert the control path at the end of the url
if u.RawQuery != "" {
if !strings.HasSuffix(u.RawQuery, "/") {
u.RawQuery += "/"
}
u.RawQuery += controlPath
} else {
if u.RawPath != "" {
if !strings.HasSuffix(u.RawPath, "/") {
u.RawPath += "/"
}
u.RawPath += controlPath
}
if !strings.HasSuffix(u.Path, "/") {
u.Path += "/"
}
u.Path += controlPath
}
}

109
base/url_test.go Normal file
View File

@@ -0,0 +1,109 @@
package base
import (
"net/url"
"testing"
"github.com/stretchr/testify/require"
)
func TestURLGetBasePath(t *testing.T) {
for _, ca := range []struct{
u *url.URL
b string
} {
{
urlMustParse("rtsp://localhost:8554/teststream"),
"teststream",
},
{
urlMustParse("rtsp://localhost:8554/test/stream"),
"test/stream",
},
{
urlMustParse("rtsp://192.168.1.99:554/test?user=tmp&password=BagRep1&channel=1&stream=0.sdp"),
"test",
},
{
urlMustParse("rtsp://192.168.1.99:554/te!st?user=tmp&password=BagRep1!&channel=1&stream=0.sdp"),
"te!st",
},
{
urlMustParse("rtsp://192.168.1.99:554/user=tmp&password=BagRep1!&channel=1&stream=0.sdp"),
"user=tmp&password=BagRep1!&channel=1&stream=0.sdp",
},
} {
b := URLGetBasePath(ca.u)
require.Equal(t, ca.b, b)
}
}
func TestURLGetBaseControlPath(t *testing.T) {
for _, ca := range []struct{
u *url.URL
b string
c string
} {
{
urlMustParse("rtsp://localhost:8554/teststream/trackID=1"),
"teststream",
"trackID=1",
},
{
urlMustParse("rtsp://localhost:8554/test/stream/trackID=1"),
"test/stream",
"trackID=1",
},
{
urlMustParse("rtsp://192.168.1.99:554/test?user=tmp&password=BagRep1&channel=1&stream=0.sdp/trackID=1"),
"test",
"trackID=1",
},
{
urlMustParse("rtsp://192.168.1.99:554/te!st?user=tmp&password=BagRep1!&channel=1&stream=0.sdp/trackID=1"),
"te!st",
"trackID=1",
},
{
urlMustParse("rtsp://192.168.1.99:554/user=tmp&password=BagRep1!&channel=1&stream=0.sdp/trackID=1"),
"user=tmp&password=BagRep1!&channel=1&stream=0.sdp",
"trackID=1",
},
} {
b, c, ok := URLGetBaseControlPath(ca.u)
require.Equal(t, true, ok)
require.Equal(t, ca.b, b)
require.Equal(t, ca.c, c)
}
}
func TestURLAddControlPath(t *testing.T) {
for _, ca := range []struct{
u *url.URL
ou *url.URL
} {
{
urlMustParse("rtsp://localhost:8554/teststream"),
urlMustParse("rtsp://localhost:8554/teststream/trackID=1"),
},
{
urlMustParse("rtsp://localhost:8554/test/stream"),
urlMustParse("rtsp://localhost:8554/test/stream/trackID=1"),
},
{
urlMustParse("rtsp://192.168.1.99:554/test?user=tmp&password=BagRep1&channel=1&stream=0.sdp"),
urlMustParse("rtsp://192.168.1.99:554/test?user=tmp&password=BagRep1&channel=1&stream=0.sdp/trackID=1"),
},
{
urlMustParse("rtsp://192.168.1.99:554/te!st?user=tmp&password=BagRep1!&channel=1&stream=0.sdp"),
urlMustParse("rtsp://192.168.1.99:554/te!st?user=tmp&password=BagRep1!&channel=1&stream=0.sdp/trackID=1"),
},
{
urlMustParse("rtsp://192.168.1.99:554/user=tmp&password=BagRep1!&channel=1&stream=0.sdp"),
urlMustParse("rtsp://192.168.1.99:554/user=tmp&password=BagRep1!&channel=1&stream=0.sdp/trackID=1"),
},
} {
URLAddControlPath(ca.u, "trackID=1")
require.Equal(t, ca.ou, ca.u)
}
}

View File

@@ -464,26 +464,7 @@ func (c *ConnClient) urlForTrack(baseUrl *url.URL, mode TransportMode, track *Tr
RawPath: baseUrl.RawPath,
RawQuery: baseUrl.RawQuery,
}
// insert the control at the end of the url
if u.RawQuery != "" {
if !strings.HasSuffix(u.RawQuery, "/") {
u.RawQuery += "/"
}
u.RawQuery += control
} else if u.RawPath != "" {
if !strings.HasSuffix(u.RawPath, "/") {
u.RawPath += "/"
}
u.RawPath += control
} else {
if !strings.HasSuffix(u.Path, "/") {
u.Path += "/"
}
u.Path += control
}
base.URLAddControlPath(u, control)
return u
}

View File

@@ -23,7 +23,7 @@ type reportReq struct {
res chan []byte
}
// RtcpReceiver is an object that helps building RTCP receiver reports, by parsing
// RtcpReceiver allows building RTCP receiver reports, by parsing
// incoming frames.
type RtcpReceiver struct {
mutex sync.Mutex