From 300a0e1b1ee16dccada8c6fa9f62f399733b795b Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Tue, 3 Nov 2020 12:05:33 +0100 Subject: [PATCH] base/URL: expose url.URL --- auth/client.go | 9 +-- auth/server.go | 4 +- base/request.go | 4 +- base/url.go | 146 +++++++++++++++++------------------------------ base/url_test.go | 8 +-- connclient.go | 12 ++-- dialer.go | 4 +- 7 files changed, 71 insertions(+), 116 deletions(-) diff --git a/auth/client.go b/auth/client.go index 1287ede7..f4a2881b 100644 --- a/auth/client.go +++ b/auth/client.go @@ -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() } diff --git a/auth/server.go b/auth/server.go index 5c72c322..af58c9ee 100644 --- a/auth/server.go +++ b/auth/server.go @@ -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 { diff --git a/base/request.go b/base/request.go index 936a2cf0..20bcb6d1 100644 --- a/base/request.go +++ b/base/request.go @@ -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 } diff --git a/base/url.go b/base/url.go index 40dce240..207d855f 100644 --- a/base/url.go +++ b/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 } diff --git a/base/url_test.go b/base/url_test.go index 868702b1..df129af2 100644 --- a/base/url_test.go +++ b/base/url_test.go @@ -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) } } diff --git a/connclient.go b/connclient.go index 8d6fe9e0..fbe407a4 100644 --- a/connclient.go +++ b/connclient.go @@ -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 } diff --git a/dialer.go b/dialer.go index 65f69dc1..9c8dd3b0 100644 --- a/dialer.go +++ b/dialer.go @@ -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,