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