Files
gortsplib/pkg/base/url.go
2020-11-15 17:26:09 +01:00

167 lines
3.5 KiB
Go

package base
import (
"fmt"
"net/url"
"strings"
)
func stringsReverseIndexByte(s string, c byte) int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == c {
return i
}
}
return -1
}
// URL is a RTSP 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) {
u, err := url.Parse(s)
if err != nil {
return nil, err
}
if u.Scheme != "rtsp" {
return nil, fmt.Errorf("wrong scheme")
}
return (*URL)(u), nil
}
// MustParseURL is like ParseURL but panics in case of errors.
func MustParseURL(s string) *URL {
u, err := ParseURL(s)
if err != nil {
panic(err)
}
return u
}
// String implements fmt.Stringer.
func (u *URL) String() string {
return (*url.URL)(u).String()
}
// Clone clones a URL.
func (u *URL) Clone() *URL {
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.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 attribute.
func (u *URL) BasePath() (string, bool) {
var path string
if u.RawPath != "" {
path = u.RawPath
} else {
path = u.Path
}
// remove leading slash
if len(path) == 0 || path[0] != '/' {
return "", false
}
path = path[1:]
return path, true
}
// 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.RawPath != "" {
pathAndQuery = u.RawPath
} else {
pathAndQuery = u.Path
}
if u.RawQuery != "" {
pathAndQuery += "?" + u.RawQuery
}
// remove leading slash
if len(pathAndQuery) == 0 || pathAndQuery[0] != '/' {
return "", "", false
}
pathAndQuery = pathAndQuery[1:]
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
}
// AddControlAttribute adds a control attribute to a RTSP url.
func (u *URL) AddControlAttribute(controlPath string) {
if controlPath[0] != '?' {
controlPath = "/" + 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
}
// RemoveControlAttribute removes a control attribute from an URL.
// We assume that the base path and control attribute are divided with a slash.
func (u *URL) RemoveControlAttribute() {
_, controlPath, ok := u.BasePathControlAttr()
if !ok {
return
}
urStr := u.String()
nu, _ := ParseURL(urStr[:len(urStr)-len(controlPath)])
*u = *nu
}