From 30711a54fa23e2590f0a990f8a9e51c08f86aba9 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Wed, 13 Mar 2019 17:25:47 +1030 Subject: [PATCH] rtmp: fix parseURL panic and improve playpath handling --- rtmp/parseurl.go | 34 ++++--- rtmp/parseurl_test.go | 200 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+), 13 deletions(-) create mode 100644 rtmp/parseurl_test.go diff --git a/rtmp/parseurl.go b/rtmp/parseurl.go index 4fea7d82..cf3e4fba 100644 --- a/rtmp/parseurl.go +++ b/rtmp/parseurl.go @@ -78,25 +78,33 @@ func parseURL(addr string) (protocol int32, host string, port uint16, app, playp if !path.IsAbs(u.Path) { return protocol, host, port, app, playpath, nil } + elems := strings.SplitN(u.Path[1:], "/", 3) app = elems[0] - if app == "" { + if len(elems[len(elems)-1]) == 0 { + elems = elems[:len(elems)-1] + } + if app == "" || len(elems) < 2 { return protocol, host, port, app, playpath, errInvalidURL } - playpath = elems[1] - if len(elems) == 3 && len(elems[2]) != 0 { - playpath = path.Join(elems[1:]...) - switch ext := path.Ext(playpath); ext { - case ".f4v", ".mp4": - playpath = "mp4:" + playpath[:len(playpath)-len(ext)] - case ".mp3": - playpath = "mp3:" + playpath[:len(playpath)-len(ext)] - case ".flv": - playpath = playpath[:len(playpath)-len(ext)] + + 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 } - if u.RawQuery != "" { - playpath += "?" + u.RawQuery + 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 { diff --git a/rtmp/parseurl_test.go b/rtmp/parseurl_test.go new file mode 100644 index 00000000..7f5e3029 --- /dev/null +++ b/rtmp/parseurl_test.go @@ -0,0 +1,200 @@ +/* +NAME + parseurl_test.go + +DESCRIPTION + See Readme.md + +AUTHOR + Dan Kortschak + +LICENSE + parseurl.go is Copyright (C) 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. +*/ +package rtmp + +import ( + "testing" +) + +var parseURLTests = []struct { + url string + wantProtocol int32 + wantHost string + wantPort uint16 + wantApp string + wantPlaypath string + wantErr error +}{ + { + url: "rtmp://addr", + wantHost: "addr", + }, + { + url: "rtmp://addr/", + wantErr: errInvalidURL, + }, + { + url: "rtmp://addr/live2", + wantErr: errInvalidURL, + }, + { + url: "rtmp://addr/live2/", + wantErr: errInvalidURL, + }, + { + url: "rtmp://addr/appname/key", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "key", + }, + { + url: "rtmp://addr/appname/instancename", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "instancename", + }, + { + url: "rtmp://addr/appname/instancename/", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "instancename", + }, + { + url: "rtmp://addr/appname/mp4:path.f4v", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "mp4:path", + }, + { + url: "rtmp://addr/appname/mp4:path.f4v?param1=value1¶m2=value2", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "mp4:path?param1=value1¶m2=value2", + }, + { + url: "rtmp://addr/appname/mp4:path/to/file.f4v", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "mp4:path/to/file", + }, + { + url: "rtmp://addr/appname/mp4:path/to/file.f4v?param1=value1¶m2=value2", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "mp4:path/to/file?param1=value1¶m2=value2", + }, + { + url: "rtmp://addr/appname/path.mp4", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "mp4:path", + }, + { + url: "rtmp://addr/appname/path.mp4?param1=value1¶m2=value2", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "mp4:path?param1=value1¶m2=value2", + }, + { + url: "rtmp://addr/appname/path/to/file.mp4", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "mp4:path/to/file", + }, + { + url: "rtmp://addr/appname/path/to/file.mp4?param1=value1¶m2=value2", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "mp4:path/to/file?param1=value1¶m2=value2", + }, + { + url: "rtmp://addr/appname/path.flv", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "path", + }, + { + url: "rtmp://addr/appname/path.flv?param1=value1¶m2=value2", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "path?param1=value1¶m2=value2", + }, + { + url: "rtmp://addr/appname/path/to/file.flv", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "path/to/file", + }, + { + url: "rtmp://addr/appname/path/to/file.flv?param1=value1¶m2=value2", + wantHost: "addr", + wantPort: 1935, + wantApp: "appname", + wantPlaypath: "path/to/file?param1=value1¶m2=value2", + }, +} + +func TestParseURL(t *testing.T) { + for _, test := range parseURLTests { + func() { + defer func() { + p := recover() + if p != nil { + t.Errorf("unexpected panic for %q: %v", test.url, p) + } + }() + + protocol, host, port, app, playpath, err := parseURL(test.url) + if err != test.wantErr { + t.Errorf("unexpected error for %q: got:%v want:%v", test.url, err, test.wantErr) + return + } + if err != nil { + return + } + if protocol != test.wantProtocol { + t.Errorf("unexpected protocol for %q: got:%v want:%v", test.url, protocol, test.wantProtocol) + } + if host != test.wantHost { + t.Errorf("unexpected host for %q: got:%v want:%v", test.url, host, test.wantHost) + } + if port != test.wantPort { + t.Errorf("unexpected port for %q: got:%v want:%v", test.url, port, test.wantPort) + } + if app != test.wantApp { + t.Errorf("unexpected app for %q: got:%v want:%v", test.url, app, test.wantApp) + } + if playpath != test.wantPlaypath { + t.Errorf("unexpected playpath for %q: got:%v want:%v", test.url, playpath, test.wantPlaypath) + } + }() + } +}