package streams import ( "errors" "regexp" "strings" "github.com/AlexxIT/go2rtc/pkg/core" ) type Handler func(source string) (core.Producer, error) var handlers = map[string]Handler{} func HandleFunc(scheme string, handler Handler) { handlers[scheme] = handler } func SupportedSchemes() []string { uniqueKeys := make(map[string]struct{}, len(handlers)+len(redirects)) for scheme := range handlers { uniqueKeys[scheme] = struct{}{} } for scheme := range redirects { uniqueKeys[scheme] = struct{}{} } resultKeys := make([]string, 0, len(uniqueKeys)) for key := range uniqueKeys { resultKeys = append(resultKeys, key) } return resultKeys } func HasProducer(url string) bool { if i := strings.IndexByte(url, ':'); i > 0 { scheme := url[:i] if _, ok := handlers[scheme]; ok { return true } if _, ok := redirects[scheme]; ok { return true } } return false } func GetProducer(url string) (core.Producer, error) { if i := strings.IndexByte(url, ':'); i > 0 { scheme := url[:i] if redirect, ok := redirects[scheme]; ok { location, err := redirect(url) if err != nil { return nil, err } if location != "" { return GetProducer(location) } } if handler, ok := handlers[scheme]; ok { return handler(url) } } return nil, errors.New("streams: unsupported scheme: " + url) } // Redirect can return: location URL or error or empty URL and error type Redirect func(url string) (string, error) var redirects = map[string]Redirect{} func RedirectFunc(scheme string, redirect Redirect) { redirects[scheme] = redirect } func Location(url string) (string, error) { if i := strings.IndexByte(url, ':'); i > 0 { scheme := url[:i] if redirect, ok := redirects[scheme]; ok { return redirect(url) } } return "", nil } // TODO: rework type ConsumerHandler func(url string) (core.Consumer, func(), error) var consumerHandlers = map[string]ConsumerHandler{} func HandleConsumerFunc(scheme string, handler ConsumerHandler) { consumerHandlers[scheme] = handler } func GetConsumer(url string) (core.Consumer, func(), error) { if i := strings.IndexByte(url, ':'); i > 0 { scheme := url[:i] if handler, ok := consumerHandlers[scheme]; ok { return handler(url) } } return nil, nil, errors.New("streams: unsupported scheme: " + url) } var insecure = map[string]bool{} func MarkInsecure(scheme string) { insecure[scheme] = true } var sanitize = regexp.MustCompile(`\s`) func Validate(source string) error { // TODO: Review the entire logic of insecure sources if i := strings.IndexByte(source, ':'); i > 0 { if insecure[source[:i]] { return errors.New("streams: source from insecure producer") } } if sanitize.MatchString(source) { return errors.New("streams: source with spaces may be insecure") } return nil }