/* NAME parseurl.go DESCRIPTION See Readme.md AUTHOR Dan Kortschak Saxon Nelson-Milton Alan Noble LICENSE parseurl.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. Derived from librtmp under the GNU Lesser General Public License 2.1 Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org Copyright (C) 2008-2009 Andrej Stepanchuk Copyright (C) 2009-2010 Howard Chu */ package rtmp import ( "errors" "fmt" "net/url" "path" "strconv" "strings" ) // Errors. var ( errInvalidPath = errors.New("invalid url path") errInvalidElements = errors.New("invalid url elements") ) // parseURL parses an RTMP URL (ok, technically it is lexing). func parseURL(addr string) (protocol int32, host string, port uint16, app, playpath string, err error) { u, err := url.Parse(addr) if err != nil { return protocol, host, port, app, playpath, fmt.Errorf("could not parse to url value: %w", err) } switch u.Scheme { case "rtmp": protocol = protoRTMP case "rtmpt": protocol = protoRTMPT case "rtmps": protocol = protoRTMPS case "rtmpe": protocol = protoRTMPE case "rtmfp": protocol = protoRTMFP case "rtmpte": protocol = protoRTMPTE case "rtmpts": protocol = protoRTMPTS default: return protocol, host, port, app, playpath, fmt.Errorf("unknown scheme: %s", u.Scheme) } host = u.Host if p := u.Port(); p != "" { pi, err := strconv.Atoi(p) if err != nil { return protocol, host, port, app, playpath, fmt.Errorf("could convert port to integer: %w", err) } port = uint16(pi) } if len(u.Path) < 1 || !path.IsAbs(u.Path) { return protocol, host, port, app, playpath, errInvalidPath } elems := strings.SplitN(u.Path[1:], "/", 3) if len(elems) < 2 || elems[0] == "" || elems[1] == "" { return protocol, host, port, app, playpath, errInvalidElements } app = elems[0] playpath = path.Join(elems[1:]...) switch ext := path.Ext(playpath); ext { case ".f4v", ".mp4": playpath = playpath[:len(playpath)-len(ext)] if !strings.HasPrefix(playpath, "mp4:") { playpath = "mp4:" + playpath } case ".mp3": playpath = playpath[:len(playpath)-len(ext)] if !strings.HasPrefix(playpath, "mp3:") { playpath = "mp3:" + playpath } case ".flv": playpath = playpath[:len(playpath)-len(ext)] } if u.RawQuery != "" { playpath += "?" + u.RawQuery } switch { case port != 0: case (protocol & featureSSL) != 0: return protocol, host, port, app, playpath, errors.New("ssl not implemented") case (protocol & featureHTTP) != 0: port = 80 default: port = 1935 } return protocol, host, port, app, playpath, nil }