mirror of
https://github.com/aler9/gortsplib
synced 2025-10-28 09:31:33 +08:00
base/URL: expose url.URL
This commit is contained in:
@@ -87,7 +87,7 @@ func NewClient(v base.HeaderValue, userinfo *url.Userinfo) (*Client, error) {
|
|||||||
// GenerateHeader generates an Authorization Header that allows to authenticate a request with
|
// GenerateHeader generates an Authorization Header that allows to authenticate a request with
|
||||||
// the given method and url.
|
// the given method and url.
|
||||||
func (ac *Client) GenerateHeader(method base.Method, ur *base.URL) base.HeaderValue {
|
func (ac *Client) GenerateHeader(method base.Method, ur *base.URL) base.HeaderValue {
|
||||||
ur = ur.CloneWithoutCredentials()
|
urStr := ur.CloneWithoutCredentials().String()
|
||||||
|
|
||||||
switch ac.method {
|
switch ac.method {
|
||||||
case headers.AuthBasic:
|
case headers.AuthBasic:
|
||||||
@@ -97,17 +97,14 @@ func (ac *Client) GenerateHeader(method base.Method, ur *base.URL) base.HeaderVa
|
|||||||
|
|
||||||
case headers.AuthDigest:
|
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)+":"+urStr))
|
||||||
|
|
||||||
return (&headers.Auth{
|
return (&headers.Auth{
|
||||||
Method: headers.AuthDigest,
|
Method: headers.AuthDigest,
|
||||||
Username: &ac.user,
|
Username: &ac.user,
|
||||||
Realm: &ac.realm,
|
Realm: &ac.realm,
|
||||||
Nonce: &ac.nonce,
|
Nonce: &ac.nonce,
|
||||||
URI: func() *string {
|
URI: &urStr,
|
||||||
v := ur.String()
|
|
||||||
return &v
|
|
||||||
}(),
|
|
||||||
Response: &response,
|
Response: &response,
|
||||||
}).Write()
|
}).Write()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,9 +125,9 @@ func (as *Server) ValidateHeader(v base.HeaderValue, method base.Method, ur *bas
|
|||||||
uri := ur.String()
|
uri := ur.String()
|
||||||
|
|
||||||
if *auth.URI != uri {
|
if *auth.URI != uri {
|
||||||
// VLC strips the control path; do another try without the control path
|
// VLC strips the control attribute; do another try without the control attribute
|
||||||
ur = ur.Clone()
|
ur = ur.Clone()
|
||||||
ur.RemoveControlPath()
|
ur.RemoveControlAttribute()
|
||||||
uri = ur.String()
|
uri = ur.String()
|
||||||
|
|
||||||
if *auth.URI != uri {
|
if *auth.URI != uri {
|
||||||
|
|||||||
@@ -111,8 +111,8 @@ func (req *Request) Read(rb *bufio.Reader) error {
|
|||||||
|
|
||||||
// Write writes a request.
|
// Write writes a request.
|
||||||
func (req Request) Write(bw *bufio.Writer) error {
|
func (req Request) Write(bw *bufio.Writer) error {
|
||||||
u := req.URL.CloneWithoutCredentials()
|
urStr := req.URL.CloneWithoutCredentials().String()
|
||||||
_, err := bw.Write([]byte(string(req.Method) + " " + u.String() + " " + rtspProtocol10 + "\r\n"))
|
_, err := bw.Write([]byte(string(req.Method) + " " + urStr + " " + rtspProtocol10 + "\r\n"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
146
base/url.go
146
base/url.go
@@ -16,9 +16,9 @@ func stringsReverseIndexByte(s string, c byte) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// URL is a RTSP URL.
|
// URL is a RTSP URL.
|
||||||
type URL struct {
|
// This is basically an HTTP url with some additional functions to handle
|
||||||
inner url.URL
|
// control attributes.
|
||||||
}
|
type URL url.URL
|
||||||
|
|
||||||
// ParseURL parses a RTSP URL.
|
// ParseURL parses a RTSP URL.
|
||||||
func ParseURL(s string) (*URL, error) {
|
func ParseURL(s string) (*URL, error) {
|
||||||
@@ -31,7 +31,7 @@ func ParseURL(s string) (*URL, error) {
|
|||||||
return nil, fmt.Errorf("wrong scheme")
|
return nil, fmt.Errorf("wrong scheme")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &URL{*u}, nil
|
return (*URL)(u), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustParseURL is like ParseURL but panics in case of errors.
|
// MustParseURL is like ParseURL but panics in case of errors.
|
||||||
@@ -45,54 +45,44 @@ func MustParseURL(s string) *URL {
|
|||||||
|
|
||||||
// String implements fmt.Stringer.
|
// String implements fmt.Stringer.
|
||||||
func (u *URL) String() string {
|
func (u *URL) String() string {
|
||||||
return u.inner.String()
|
return (*url.URL)(u).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone clones a URL.
|
// Clone clones a URL.
|
||||||
func (u *URL) Clone() *URL {
|
func (u *URL) Clone() *URL {
|
||||||
return &URL{url.URL{
|
return (*URL)(&url.URL{
|
||||||
Scheme: u.inner.Scheme,
|
Scheme: u.Scheme,
|
||||||
Opaque: u.inner.Opaque,
|
Opaque: u.Opaque,
|
||||||
User: u.inner.User,
|
User: u.User,
|
||||||
Host: u.inner.Host,
|
Host: u.Host,
|
||||||
Path: u.inner.Path,
|
Path: u.Path,
|
||||||
RawPath: u.inner.RawPath,
|
RawPath: u.RawPath,
|
||||||
ForceQuery: u.inner.ForceQuery,
|
ForceQuery: u.ForceQuery,
|
||||||
RawQuery: u.inner.RawQuery,
|
RawQuery: u.RawQuery,
|
||||||
}}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloneWithoutCredentials clones a URL without its credentials.
|
// CloneWithoutCredentials clones a URL without its credentials.
|
||||||
func (u *URL) CloneWithoutCredentials() *URL {
|
func (u *URL) CloneWithoutCredentials() *URL {
|
||||||
return &URL{url.URL{
|
return (*URL)(&url.URL{
|
||||||
Scheme: u.inner.Scheme,
|
Scheme: u.Scheme,
|
||||||
Opaque: u.inner.Opaque,
|
Opaque: u.Opaque,
|
||||||
Host: u.inner.Host,
|
Host: u.Host,
|
||||||
Path: u.inner.Path,
|
Path: u.Path,
|
||||||
RawPath: u.inner.RawPath,
|
RawPath: u.RawPath,
|
||||||
ForceQuery: u.inner.ForceQuery,
|
ForceQuery: u.ForceQuery,
|
||||||
RawQuery: u.inner.RawQuery,
|
RawQuery: u.RawQuery,
|
||||||
}}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// Host returns the host of a RTSP URL.
|
|
||||||
func (u *URL) Host() string {
|
|
||||||
return u.inner.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
// User returns the credentials of a RTSP URL.
|
|
||||||
func (u *URL) User() *url.Userinfo {
|
|
||||||
return u.inner.User
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasePath returns the base path of a RTSP URL.
|
// BasePath returns the base path of a RTSP URL.
|
||||||
// We assume that the URL doesn't contain a control path.
|
// We assume that the URL doesn't contain a control attribute.
|
||||||
func (u *URL) BasePath() (string, bool) {
|
func (u *URL) BasePath() (string, bool) {
|
||||||
var path string
|
var path string
|
||||||
if u.inner.RawPath != "" {
|
if u.RawPath != "" {
|
||||||
path = u.inner.RawPath
|
path = u.RawPath
|
||||||
} else {
|
} else {
|
||||||
path = u.inner.Path
|
path = u.Path
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove leading slash
|
// remove leading slash
|
||||||
@@ -104,17 +94,18 @@ func (u *URL) BasePath() (string, bool) {
|
|||||||
return path, true
|
return path, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseControlPath returns the base path and the control path of a RTSP URL.
|
// BasePathControlAttr returns the base path and the control attribute of a RTSP URL.
|
||||||
// We assume that the URL contains a control path.
|
// We assume that the URL contains a control attribute.
|
||||||
func (u *URL) BaseControlPath() (string, string, bool) {
|
// We assume that the base path and control attribute are divided with a slash.
|
||||||
|
func (u *URL) BasePathControlAttr() (string, string, bool) {
|
||||||
var pathAndQuery string
|
var pathAndQuery string
|
||||||
if u.inner.RawPath != "" {
|
if u.RawPath != "" {
|
||||||
pathAndQuery = u.inner.RawPath
|
pathAndQuery = u.RawPath
|
||||||
} else {
|
} else {
|
||||||
pathAndQuery = u.inner.Path
|
pathAndQuery = u.Path
|
||||||
}
|
}
|
||||||
if u.inner.RawQuery != "" {
|
if u.RawQuery != "" {
|
||||||
pathAndQuery += "?" + u.inner.RawQuery
|
pathAndQuery += "?" + u.RawQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove leading slash
|
// remove leading slash
|
||||||
@@ -148,60 +139,27 @@ func (u *URL) BaseControlPath() (string, string, bool) {
|
|||||||
return basePath, controlPath, true
|
return basePath, controlPath, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetHost sets the host of a RTSP URL.
|
// AddControlAttribute adds a control attribute to a RTSP url.
|
||||||
func (u *URL) SetHost(host string) {
|
func (u *URL) AddControlAttribute(controlPath string) {
|
||||||
u.inner.Host = host
|
if controlPath[0] != '?' {
|
||||||
}
|
controlPath = "/" + controlPath
|
||||||
|
|
||||||
// SetUser sets the credentials of a RTSP URL.
|
|
||||||
func (u *URL) SetUser(user *url.Userinfo) {
|
|
||||||
u.inner.User = user
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddControlPath adds a control path to a RTSP url.
|
|
||||||
func (u *URL) AddControlPath(controlPath string) {
|
|
||||||
// special case: query
|
|
||||||
if controlPath[0] == '?' {
|
|
||||||
u.inner.RawQuery += controlPath[1:]
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// always insert the control path at the end of the url
|
// insert the control attribute at the end of the url
|
||||||
if u.inner.RawQuery != "" {
|
// if there's a query, insert it after the query
|
||||||
if !strings.HasSuffix(u.inner.RawQuery, "/") {
|
// otherwise insert it after the path
|
||||||
u.inner.RawQuery += "/"
|
nu, _ := ParseURL(u.String() + controlPath)
|
||||||
}
|
*u = *nu
|
||||||
u.inner.RawQuery += controlPath
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if u.inner.RawPath != "" {
|
|
||||||
if !strings.HasSuffix(u.inner.RawPath, "/") {
|
|
||||||
u.inner.RawPath += "/"
|
|
||||||
}
|
|
||||||
u.inner.RawPath += controlPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasSuffix(u.inner.Path, "/") {
|
|
||||||
u.inner.Path += "/"
|
|
||||||
}
|
|
||||||
u.inner.Path += controlPath
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveControlPath removes a control path from an URL.
|
// RemoveControlAttribute removes a control attribute from an URL.
|
||||||
func (u *URL) RemoveControlPath() {
|
func (u *URL) RemoveControlAttribute() {
|
||||||
_, controlPath, ok := u.BaseControlPath()
|
_, controlPath, ok := u.BasePathControlAttr()
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(u.inner.RawQuery, controlPath) {
|
urStr := u.String()
|
||||||
u.inner.RawQuery = u.inner.RawQuery[:len(u.inner.RawQuery)-len(controlPath)]
|
nu, _ := ParseURL(urStr[:len(urStr)-len(controlPath)])
|
||||||
|
*u = *nu
|
||||||
} else if strings.HasSuffix(u.inner.RawPath, controlPath) {
|
|
||||||
u.inner.RawPath = u.inner.RawPath[:len(u.inner.RawPath)-len(controlPath)]
|
|
||||||
|
|
||||||
} else if strings.HasSuffix(u.inner.Path, controlPath) {
|
|
||||||
u.inner.Path = u.inner.Path[:len(u.inner.Path)-len(controlPath)]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func TestURLBasePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestURLBaseControlPath(t *testing.T) {
|
func TestURLBasePathControlAttr(t *testing.T) {
|
||||||
for _, ca := range []struct {
|
for _, ca := range []struct {
|
||||||
u *URL
|
u *URL
|
||||||
b string
|
b string
|
||||||
@@ -70,14 +70,14 @@ func TestURLBaseControlPath(t *testing.T) {
|
|||||||
"trackID=1",
|
"trackID=1",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
b, c, ok := ca.u.BaseControlPath()
|
b, c, ok := ca.u.BasePathControlAttr()
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
require.Equal(t, ca.b, b)
|
require.Equal(t, ca.b, b)
|
||||||
require.Equal(t, ca.c, c)
|
require.Equal(t, ca.c, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestURLAddControlPath(t *testing.T) {
|
func TestURLAddControlAttribute(t *testing.T) {
|
||||||
for _, ca := range []struct {
|
for _, ca := range []struct {
|
||||||
control string
|
control string
|
||||||
u *URL
|
u *URL
|
||||||
@@ -119,7 +119,7 @@ func TestURLAddControlPath(t *testing.T) {
|
|||||||
MustParseURL("rtsp://192.168.1.99:554/test?ctype=video"),
|
MustParseURL("rtsp://192.168.1.99:554/test?ctype=video"),
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
ca.u.AddControlPath(ca.control)
|
ca.u.AddControlAttribute(ca.control)
|
||||||
require.Equal(t, ca.ou, ca.u)
|
require.Equal(t, ca.ou, ca.u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -324,8 +324,8 @@ func (c *ConnClient) Do(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setup authentication
|
// setup authentication
|
||||||
if res.StatusCode == base.StatusUnauthorized && req.URL.User() != nil && c.auth == nil {
|
if res.StatusCode == base.StatusUnauthorized && req.URL.User != nil && c.auth == nil {
|
||||||
auth, err := auth.NewClient(res.Header["WWW-Authenticate"], req.URL.User())
|
auth, err := auth.NewClient(res.Header["WWW-Authenticate"], req.URL.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to setup authentication: %s", err)
|
return nil, fmt.Errorf("unable to setup authentication: %s", err)
|
||||||
}
|
}
|
||||||
@@ -440,14 +440,14 @@ func (c *ConnClient) urlForTrack(baseUrl *base.URL, mode TransportMode, track *T
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy host and credentials
|
// copy host and credentials
|
||||||
newUrl.SetHost(baseUrl.Host())
|
newUrl.Host = baseUrl.Host
|
||||||
newUrl.SetUser(baseUrl.User())
|
newUrl.User = baseUrl.User
|
||||||
return newUrl
|
return newUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
// control attribute contains a control path
|
// control attribute contains a control attribute
|
||||||
newUrl := baseUrl.Clone()
|
newUrl := baseUrl.Clone()
|
||||||
newUrl.AddControlPath(control)
|
newUrl.AddControlAttribute(control)
|
||||||
return newUrl
|
return newUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func (d Dialer) DialRead(address string, proto StreamProtocol) (*ConnClient, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn, err := NewConnClient(ConnClientConf{
|
conn, err := NewConnClient(ConnClientConf{
|
||||||
Host: u.Host(),
|
Host: u.Host,
|
||||||
ReadTimeout: d.ReadTimeout,
|
ReadTimeout: d.ReadTimeout,
|
||||||
WriteTimeout: d.WriteTimeout,
|
WriteTimeout: d.WriteTimeout,
|
||||||
ReadBufferCount: d.ReadBufferCount,
|
ReadBufferCount: d.ReadBufferCount,
|
||||||
@@ -117,7 +117,7 @@ func (d Dialer) DialPublish(address string, proto StreamProtocol, tracks Tracks)
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn, err := NewConnClient(ConnClientConf{
|
conn, err := NewConnClient(ConnClientConf{
|
||||||
Host: u.Host(),
|
Host: u.Host,
|
||||||
ReadTimeout: d.ReadTimeout,
|
ReadTimeout: d.ReadTimeout,
|
||||||
WriteTimeout: d.WriteTimeout,
|
WriteTimeout: d.WriteTimeout,
|
||||||
ReadBufferCount: d.ReadBufferCount,
|
ReadBufferCount: d.ReadBufferCount,
|
||||||
|
|||||||
Reference in New Issue
Block a user