From 8f04264fe5ccd2ee3b70445e52dd8a504df55608 Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Sat, 11 Jan 2025 17:29:48 +0100 Subject: [PATCH] webrtxc: fix MTX_QUERY not set when reading or publishing (#4138) (#3937) (#4141) --- internal/api/api.go | 12 +-- internal/auth/manager.go | 88 +++----------------- internal/auth/manager_test.go | 111 ++++++++++++++----------- internal/auth/request.go | 84 +++++++++++++++++++ internal/defs/path.go | 49 ----------- internal/defs/path_access_request.go | 96 +++++++++++++++++++++ internal/metrics/metrics.go | 12 +-- internal/playback/server.go | 14 ++-- internal/pprof/pprof.go | 12 +-- internal/servers/hls/http_server.go | 16 ++-- internal/servers/hls/muxer.go | 2 +- internal/servers/hls/server_test.go | 59 ++++++------- internal/servers/rtmp/server_test.go | 51 ++++++------ internal/servers/rtsp/conn.go | 21 +++-- internal/servers/rtsp/server_test.go | 75 ++++++++++------- internal/servers/rtsp/session.go | 48 ++++++----- internal/servers/srt/conn.go | 4 +- internal/servers/srt/server_test.go | 53 ++++++------ internal/servers/webrtc/http_server.go | 16 ++-- internal/servers/webrtc/server_test.go | 56 ++++++------- internal/servers/webrtc/session.go | 38 +++++---- internal/test/auth_manager.go | 2 +- internal/test/formats.go | 6 +- internal/test/logger.go | 2 +- internal/test/medias.go | 8 +- internal/test/path_manager.go | 35 ++++++++ internal/test/tls_cert.go | 8 +- 27 files changed, 560 insertions(+), 418 deletions(-) create mode 100644 internal/auth/request.go create mode 100644 internal/defs/path_access_request.go create mode 100644 internal/test/path_manager.go diff --git a/internal/api/api.go b/internal/api/api.go index 2f9dfbfbde6..23c6e9e365e 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -286,11 +286,13 @@ func (a *API) middlewareOrigin(ctx *gin.Context) { } func (a *API) middlewareAuth(ctx *gin.Context) { - err := a.AuthManager.Authenticate(&auth.Request{ - IP: net.ParseIP(ctx.ClientIP()), - Action: conf.AuthActionAPI, - HTTPRequest: ctx.Request, - }) + req := &auth.Request{ + IP: net.ParseIP(ctx.ClientIP()), + Action: conf.AuthActionAPI, + } + req.FillFromHTTPRequest(ctx.Request) + + err := a.AuthManager.Authenticate(req) if err != nil { if err.(*auth.Error).AskCredentials { //nolint:errorlint ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) diff --git a/internal/auth/manager.go b/internal/auth/manager.go index c51541dc469..297fd57462d 100644 --- a/internal/auth/manager.go +++ b/internal/auth/manager.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "net" "net/http" "net/url" "regexp" @@ -16,7 +15,6 @@ import ( "github.com/MicahParks/keyfunc/v3" "github.com/bluenviron/gortsplib/v4/pkg/auth" - "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/headers" "github.com/bluenviron/mediamtx/internal/conf" "github.com/golang-jwt/jwt/v5" @@ -31,50 +29,6 @@ const ( jwtRefreshPeriod = 60 * 60 * time.Second ) -func addJWTFromAuthorization(rawQuery string, auth string) string { - jwt := strings.TrimPrefix(auth, "Bearer ") - if rawQuery != "" { - if v, err := url.ParseQuery(rawQuery); err == nil && v.Get("jwt") == "" { - v.Set("jwt", jwt) - return v.Encode() - } - } - return url.Values{"jwt": []string{jwt}}.Encode() -} - -// Protocol is a protocol. -type Protocol string - -// protocols. -const ( - ProtocolRTSP Protocol = "rtsp" - ProtocolRTMP Protocol = "rtmp" - ProtocolHLS Protocol = "hls" - ProtocolWebRTC Protocol = "webrtc" - ProtocolSRT Protocol = "srt" -) - -// Request is an authentication request. -type Request struct { - User string - Pass string - IP net.IP - Action conf.AuthAction - - // only for ActionPublish, ActionRead, ActionPlayback - Path string - Protocol Protocol - ID *uuid.UUID - Query string - - // RTSP only - RTSPRequest *base.Request - RTSPNonce string - - // HTTP only - HTTPRequest *http.Request -} - // Error is a authentication error. type Error struct { Message string @@ -171,38 +125,11 @@ func (m *Manager) ReloadInternalUsers(u []conf.AuthInternalUser) { // Authenticate authenticates a request. func (m *Manager) Authenticate(req *Request) error { - var rtspAuthHeader headers.Authorization - - if req.RTSPRequest != nil { - err := rtspAuthHeader.Unmarshal(req.RTSPRequest.Header["Authorization"]) - if err == nil { - if rtspAuthHeader.Method == headers.AuthMethodBasic { - req.User = rtspAuthHeader.BasicUser - req.Pass = rtspAuthHeader.BasicPass - } else { // digest - req.User = rtspAuthHeader.Username - } - } - } else if req.HTTPRequest != nil { - req.User, req.Pass, _ = req.HTTPRequest.BasicAuth() - req.Query = req.HTTPRequest.URL.RawQuery - - if h := req.HTTPRequest.Header.Get("Authorization"); strings.HasPrefix(h, "Bearer ") { - // support passing username and password through Authorization header - if parts := strings.Split(strings.TrimPrefix(h, "Bearer "), ":"); len(parts) == 2 { - req.User = parts[0] - req.Pass = parts[1] - } else { - req.Query = addJWTFromAuthorization(req.Query, h) - } - } - } - var err error switch m.Method { case conf.AuthMethodInternal: - err = m.authenticateInternal(req, &rtspAuthHeader) + err = m.authenticateInternal(req) case conf.AuthMethodHTTP: err = m.authenticateHTTP(req) @@ -221,7 +148,16 @@ func (m *Manager) Authenticate(req *Request) error { return nil } -func (m *Manager) authenticateInternal(req *Request, rtspAuthHeader *headers.Authorization) error { +func (m *Manager) authenticateInternal(req *Request) error { + var rtspAuthHeader *headers.Authorization + if req.RTSPRequest != nil { + var tmp headers.Authorization + err := tmp.Unmarshal(req.RTSPRequest.Header["Authorization"]) + if err == nil { + rtspAuthHeader = &tmp + } + } + m.mutex.RLock() defer m.mutex.RUnlock() @@ -252,7 +188,7 @@ func (m *Manager) authenticateWithUser( } if u.User != "any" { - if req.RTSPRequest != nil && rtspAuthHeader.Method == headers.AuthMethodDigest { + if req.RTSPRequest != nil && rtspAuthHeader != nil && rtspAuthHeader.Method == headers.AuthMethodDigest { err := auth.Validate( req.RTSPRequest, string(u.User), diff --git a/internal/auth/manager_test.go b/internal/auth/manager_test.go index cdc9b718cd5..f53a53345dc 100644 --- a/internal/auth/manager_test.go +++ b/internal/auth/manager_test.go @@ -143,48 +143,63 @@ func TestAuthInternal(t *testing.T) { } func TestAuthInternalRTSPDigest(t *testing.T) { - m := Manager{ - Method: conf.AuthMethodInternal, - InternalUsers: []conf.AuthInternalUser{ - { - User: "myuser", - Pass: "mypass", - IPs: conf.IPNetworks{mustParseCIDR("127.1.1.1/32")}, - Permissions: []conf.AuthInternalUserPermission{{ - Action: conf.AuthActionPublish, - Path: "mypath", - }}, - }, - }, - HTTPAddress: "", - RTSPAuthMethods: []auth.ValidateMethod{auth.ValidateMethodDigestMD5}, - } + for _, ca := range []string{"ok", "invalid"} { + t.Run(ca, func(t *testing.T) { + m := Manager{ + Method: conf.AuthMethodInternal, + InternalUsers: []conf.AuthInternalUser{ + { + User: "myuser", + Pass: "mypass", + IPs: conf.IPNetworks{mustParseCIDR("127.1.1.1/32")}, + Permissions: []conf.AuthInternalUserPermission{{ + Action: conf.AuthActionPublish, + Path: "mypath", + }}, + }, + }, + HTTPAddress: "", + RTSPAuthMethods: []auth.ValidateMethod{auth.ValidateMethodDigestMD5}, + } - u, err := base.ParseURL("rtsp://127.0.0.1:8554/mypath") - require.NoError(t, err) + u, err := base.ParseURL("rtsp://127.0.0.1:8554/mypath") + require.NoError(t, err) - s, err := auth.NewSender( - auth.GenerateWWWAuthenticate([]auth.ValidateMethod{auth.ValidateMethodDigestMD5}, "IPCAM", "mynonce"), - "myuser", - "mypass", - ) - require.NoError(t, err) + req := &base.Request{ + Method: "ANNOUNCE", + URL: u, + } - req := &base.Request{ - Method: "ANNOUNCE", - URL: u, - } + if ca == "ok" { + var s *auth.Sender + s, err = auth.NewSender( + auth.GenerateWWWAuthenticate([]auth.ValidateMethod{auth.ValidateMethodDigestMD5}, "IPCAM", "mynonce"), + "myuser", + "mypass", + ) + require.NoError(t, err) + s.AddAuthorization(req) + } else { + req.Header = base.Header{"Authorization": base.HeaderValue{"garbage"}} + } - s.AddAuthorization(req) + req1 := &Request{ + IP: net.ParseIP("127.1.1.1"), + Action: conf.AuthActionPublish, + Path: "mypath", + RTSPRequest: req, + RTSPNonce: "mynonce", + } + req1.FillFromRTSPRequest(req) + err = m.Authenticate(req1) - err = m.Authenticate(&Request{ - IP: net.ParseIP("127.1.1.1"), - Action: conf.AuthActionPublish, - Path: "mypath", - RTSPRequest: req, - RTSPNonce: "mynonce", - }) - require.NoError(t, err) + if ca == "ok" { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } } func TestAuthInternalCredentialsInBearer(t *testing.T) { @@ -205,16 +220,17 @@ func TestAuthInternalCredentialsInBearer(t *testing.T) { RTSPAuthMethods: []auth.ValidateMethod{auth.ValidateMethodDigestMD5}, } - err := m.Authenticate(&Request{ + req := &Request{ IP: net.ParseIP("127.1.1.1"), Action: conf.AuthActionPublish, Path: "mypath", Protocol: ProtocolRTSP, - HTTPRequest: &http.Request{ - Header: http.Header{"Authorization": []string{"Bearer myuser:mypass"}}, - URL: &url.URL{}, - }, + } + req.FillFromHTTPRequest(&http.Request{ + Header: http.Header{"Authorization": []string{"Bearer myuser:mypass"}}, + URL: &url.URL{}, }) + err := m.Authenticate(req) require.NoError(t, err) } @@ -399,16 +415,17 @@ func TestAuthJWT(t *testing.T) { Query: "param=value&jwt=" + ss, }) } else { - err = m.Authenticate(&Request{ + req := &Request{ IP: net.ParseIP("127.0.0.1"), Action: conf.AuthActionPublish, Path: "mypath", Protocol: ProtocolWebRTC, - HTTPRequest: &http.Request{ - Header: http.Header{"Authorization": []string{"Bearer " + ss}}, - URL: &url.URL{}, - }, + } + req.FillFromHTTPRequest(&http.Request{ + Header: http.Header{"Authorization": []string{"Bearer " + ss}}, + URL: &url.URL{}, }) + err = m.Authenticate(req) } require.NoError(t, err) }) diff --git a/internal/auth/request.go b/internal/auth/request.go new file mode 100644 index 00000000000..fd59e3ff1d4 --- /dev/null +++ b/internal/auth/request.go @@ -0,0 +1,84 @@ +package auth + +import ( + "net" + "net/http" + "net/url" + "strings" + + "github.com/bluenviron/gortsplib/v4/pkg/base" + "github.com/bluenviron/gortsplib/v4/pkg/headers" + "github.com/bluenviron/mediamtx/internal/conf" + "github.com/google/uuid" +) + +func addJWTFromAuthorization(rawQuery string, auth string) string { + jwt := strings.TrimPrefix(auth, "Bearer ") + if rawQuery != "" { + if v, err := url.ParseQuery(rawQuery); err == nil && v.Get("jwt") == "" { + v.Set("jwt", jwt) + return v.Encode() + } + } + return url.Values{"jwt": []string{jwt}}.Encode() +} + +// Protocol is a protocol. +type Protocol string + +// protocols. +const ( + ProtocolRTSP Protocol = "rtsp" + ProtocolRTMP Protocol = "rtmp" + ProtocolHLS Protocol = "hls" + ProtocolWebRTC Protocol = "webrtc" + ProtocolSRT Protocol = "srt" +) + +// Request is an authentication request. +type Request struct { + User string + Pass string + IP net.IP + Action conf.AuthAction + + // only for ActionPublish, ActionRead, ActionPlayback + Path string + Protocol Protocol + ID *uuid.UUID + Query string + + // RTSP only + RTSPRequest *base.Request + RTSPNonce string +} + +// FillFromRTSPRequest fills User and Pass from a RTSP request. +func (r *Request) FillFromRTSPRequest(rt *base.Request) { + var rtspAuthHeader headers.Authorization + err := rtspAuthHeader.Unmarshal(rt.Header["Authorization"]) + if err == nil { + if rtspAuthHeader.Method == headers.AuthMethodBasic { + r.User = rtspAuthHeader.BasicUser + r.Pass = rtspAuthHeader.BasicPass + } else { + r.User = rtspAuthHeader.Username + } + } +} + +// FillFromHTTPRequest fills Query, User and Pass from an HTTP request. +func (r *Request) FillFromHTTPRequest(h *http.Request) { + r.Query = h.URL.RawQuery + r.User, r.Pass, _ = h.BasicAuth() + + if h := h.Header.Get("Authorization"); strings.HasPrefix(h, "Bearer ") { + // support passing user and password through the Authorization header + if parts := strings.Split(strings.TrimPrefix(h, "Bearer "), ":"); len(parts) == 2 { + r.User = parts[0] + r.Pass = parts[1] + } else { // move Authorization header to Query + r.Query = addJWTFromAuthorization(r.Query, h) + } + } +} diff --git a/internal/defs/path.go b/internal/defs/path.go index 28568583015..74d7492bd3e 100644 --- a/internal/defs/path.go +++ b/internal/defs/path.go @@ -2,14 +2,9 @@ package defs import ( "fmt" - "net" - "net/http" - "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/description" - "github.com/google/uuid" - "github.com/bluenviron/mediamtx/internal/auth" "github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/externalcmd" "github.com/bluenviron/mediamtx/internal/stream" @@ -36,50 +31,6 @@ type Path interface { RemoveReader(req PathRemoveReaderReq) } -// PathAccessRequest is a path access request. -type PathAccessRequest struct { - Name string - Query string - Publish bool - SkipAuth bool - - // only if skipAuth = false - User string - Pass string - IP net.IP - Proto auth.Protocol - ID *uuid.UUID - - // RTSP only - RTSPRequest *base.Request - RTSPNonce string - - // HTTP only - HTTPRequest *http.Request -} - -// ToAuthRequest converts a path access request into an authentication request. -func (r *PathAccessRequest) ToAuthRequest() *auth.Request { - return &auth.Request{ - User: r.User, - Pass: r.Pass, - IP: r.IP, - Action: func() conf.AuthAction { - if r.Publish { - return conf.AuthActionPublish - } - return conf.AuthActionRead - }(), - Path: r.Name, - Protocol: r.Proto, - ID: r.ID, - Query: r.Query, - RTSPRequest: r.RTSPRequest, - RTSPNonce: r.RTSPNonce, - HTTPRequest: r.HTTPRequest, - } -} - // PathFindPathConfRes contains the response of FindPathConf(). type PathFindPathConfRes struct { Conf *conf.Path diff --git a/internal/defs/path_access_request.go b/internal/defs/path_access_request.go new file mode 100644 index 00000000000..daf17f2ef97 --- /dev/null +++ b/internal/defs/path_access_request.go @@ -0,0 +1,96 @@ +package defs + +import ( + "net" + "net/http" + "net/url" + "strings" + + "github.com/bluenviron/gortsplib/v4/pkg/base" + "github.com/bluenviron/gortsplib/v4/pkg/headers" + "github.com/bluenviron/mediamtx/internal/auth" + "github.com/bluenviron/mediamtx/internal/conf" + "github.com/google/uuid" +) + +func addJWTFromAuthorization(rawQuery string, auth string) string { + jwt := strings.TrimPrefix(auth, "Bearer ") + if rawQuery != "" { + if v, err := url.ParseQuery(rawQuery); err == nil && v.Get("jwt") == "" { + v.Set("jwt", jwt) + return v.Encode() + } + } + return url.Values{"jwt": []string{jwt}}.Encode() +} + +// PathAccessRequest is a path access request. +type PathAccessRequest struct { + Name string + Query string + Publish bool + SkipAuth bool + + // only if skipAuth = false + User string + Pass string + IP net.IP + Proto auth.Protocol + ID *uuid.UUID + + // RTSP only + RTSPRequest *base.Request + RTSPNonce string +} + +// ToAuthRequest converts a path access request into an authentication request. +func (r *PathAccessRequest) ToAuthRequest() *auth.Request { + return &auth.Request{ + User: r.User, + Pass: r.Pass, + IP: r.IP, + Action: func() conf.AuthAction { + if r.Publish { + return conf.AuthActionPublish + } + return conf.AuthActionRead + }(), + Path: r.Name, + Protocol: r.Proto, + ID: r.ID, + Query: r.Query, + RTSPRequest: r.RTSPRequest, + RTSPNonce: r.RTSPNonce, + } +} + +// FillFromRTSPRequest fills User and Pass from a RTSP request. +func (r *PathAccessRequest) FillFromRTSPRequest(rt *base.Request) { + var rtspAuthHeader headers.Authorization + err := rtspAuthHeader.Unmarshal(rt.Header["Authorization"]) + if err == nil { + if rtspAuthHeader.Method == headers.AuthMethodBasic { + r.User = rtspAuthHeader.BasicUser + r.Pass = rtspAuthHeader.BasicPass + } else { + r.User = rtspAuthHeader.Username + } + } +} + +// FillFromHTTPRequest fills Query, User and Pass from an HTTP request. +func (r *PathAccessRequest) FillFromHTTPRequest(h *http.Request) { + r.Query = h.URL.RawQuery + r.User, r.Pass, _ = h.BasicAuth() + + // move Authorization header from headers to Query + if h := h.Header.Get("Authorization"); strings.HasPrefix(h, "Bearer ") { + // support passing user and password through the Authorization header + if parts := strings.Split(strings.TrimPrefix(h, "Bearer "), ":"); len(parts) == 2 { + r.User = parts[0] + r.Pass = parts[1] + } else { + r.Query = addJWTFromAuthorization(r.Query, h) + } + } +} diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index 1c0110db798..cadabc1a785 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -122,11 +122,13 @@ func (m *Metrics) middlewareOrigin(ctx *gin.Context) { } func (m *Metrics) middlewareAuth(ctx *gin.Context) { - err := m.AuthManager.Authenticate(&auth.Request{ - IP: net.ParseIP(ctx.ClientIP()), - Action: conf.AuthActionMetrics, - HTTPRequest: ctx.Request, - }) + req := &auth.Request{ + IP: net.ParseIP(ctx.ClientIP()), + Action: conf.AuthActionMetrics, + } + req.FillFromHTTPRequest(ctx.Request) + + err := m.AuthManager.Authenticate(req) if err != nil { if err.(*auth.Error).AskCredentials { //nolint:errorlint ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) diff --git a/internal/playback/server.go b/internal/playback/server.go index 7874e49d40c..76fdf6fad78 100644 --- a/internal/playback/server.go +++ b/internal/playback/server.go @@ -117,12 +117,14 @@ func (s *Server) middlewareOrigin(ctx *gin.Context) { } func (s *Server) doAuth(ctx *gin.Context, pathName string) bool { - err := s.AuthManager.Authenticate(&auth.Request{ - IP: net.ParseIP(ctx.ClientIP()), - Action: conf.AuthActionPlayback, - Path: pathName, - HTTPRequest: ctx.Request, - }) + req := &auth.Request{ + IP: net.ParseIP(ctx.ClientIP()), + Action: conf.AuthActionPlayback, + Path: pathName, + } + req.FillFromHTTPRequest(ctx.Request) + + err := s.AuthManager.Authenticate(req) if err != nil { if err.(*auth.Error).AskCredentials { //nolint:errorlint ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) diff --git a/internal/pprof/pprof.go b/internal/pprof/pprof.go index 1cd93b570c7..acf47274a07 100644 --- a/internal/pprof/pprof.go +++ b/internal/pprof/pprof.go @@ -97,11 +97,13 @@ func (pp *PPROF) middlewareOrigin(ctx *gin.Context) { } func (pp *PPROF) middlewareAuth(ctx *gin.Context) { - err := pp.AuthManager.Authenticate(&auth.Request{ - IP: net.ParseIP(ctx.ClientIP()), - Action: conf.AuthActionPprof, - HTTPRequest: ctx.Request, - }) + req := &auth.Request{ + IP: net.ParseIP(ctx.ClientIP()), + Action: conf.AuthActionPprof, + } + req.FillFromHTTPRequest(ctx.Request) + + err := pp.AuthManager.Authenticate(req) if err != nil { if err.(*auth.Error).AskCredentials { //nolint:errorlint ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) diff --git a/internal/servers/hls/http_server.go b/internal/servers/hls/http_server.go index df122a1e317..3e8b1057758 100644 --- a/internal/servers/hls/http_server.go +++ b/internal/servers/hls/http_server.go @@ -147,14 +147,16 @@ func (s *httpServer) onRequest(ctx *gin.Context) { return } + req := defs.PathAccessRequest{ + Name: dir, + Publish: false, + IP: net.ParseIP(ctx.ClientIP()), + Proto: auth.ProtocolHLS, + } + req.FillFromHTTPRequest(ctx.Request) + pathConf, err := s.pathManager.FindPathConf(defs.PathFindPathConfReq{ - AccessRequest: defs.PathAccessRequest{ - Name: dir, - Publish: false, - IP: net.ParseIP(ctx.ClientIP()), - Proto: auth.ProtocolHLS, - HTTPRequest: ctx.Request, - }, + AccessRequest: req, }) if err != nil { var terr *auth.Error diff --git a/internal/servers/hls/muxer.go b/internal/servers/hls/muxer.go index 5614843e702..5806133c45b 100644 --- a/internal/servers/hls/muxer.go +++ b/internal/servers/hls/muxer.go @@ -124,8 +124,8 @@ func (m *muxer) runInner() error { Author: m, AccessRequest: defs.PathAccessRequest{ Name: m.pathName, - SkipAuth: true, Query: m.query, + SkipAuth: true, }, }) if err != nil { diff --git a/internal/servers/hls/server_test.go b/internal/servers/hls/server_test.go index 70ee9331040..d1460a248d6 100644 --- a/internal/servers/hls/server_test.go +++ b/internal/servers/hls/server_test.go @@ -24,7 +24,7 @@ import ( type dummyPath struct{} func (pa *dummyPath) Name() string { - return "mystream" + return "teststream" } func (pa *dummyPath) SafeConf() *conf.Path { @@ -48,19 +48,6 @@ func (pa *dummyPath) RemovePublisher(_ defs.PathRemovePublisherReq) { func (pa *dummyPath) RemoveReader(_ defs.PathRemoveReaderReq) { } -type dummyPathManager struct { - findPathConf func(req defs.PathFindPathConfReq) (*conf.Path, error) - addReader func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) -} - -func (pm *dummyPathManager) FindPathConf(req defs.PathFindPathConfReq) (*conf.Path, error) { - return pm.findPathConf(req) -} - -func (pm *dummyPathManager) AddReader(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { - return pm.addReader(req) -} - func TestPreflightRequest(t *testing.T) { s := &Server{ Address: "127.0.0.1:8888", @@ -103,12 +90,12 @@ func TestServerNotFound(t *testing.T) { "always remux on", } { t.Run(ca, func(t *testing.T) { - pm := &dummyPathManager{ - findPathConf: func(req defs.PathFindPathConfReq) (*conf.Path, error) { + pm := &test.PathManager{ + FindPathConfImpl: func(req defs.PathFindPathConfReq) (*conf.Path, error) { require.Equal(t, "nonexisting", req.AccessRequest.Name) return &conf.Path{}, nil }, - addReader: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + AddReaderImpl: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { require.Equal(t, "nonexisting", req.AccessRequest.Name) return nil, nil, fmt.Errorf("not found") }, @@ -176,13 +163,17 @@ func TestServerRead(t *testing.T) { ) require.NoError(t, err) - pm := &dummyPathManager{ - findPathConf: func(req defs.PathFindPathConfReq) (*conf.Path, error) { - require.Equal(t, "mystream", req.AccessRequest.Name) + pm := &test.PathManager{ + FindPathConfImpl: func(req defs.PathFindPathConfReq) (*conf.Path, error) { + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) return &conf.Path{}, nil }, - addReader: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { - require.Equal(t, "mystream", req.AccessRequest.Name) + AddReaderImpl: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) return &dummyPath{}, str, nil }, } @@ -210,7 +201,7 @@ func TestServerRead(t *testing.T) { defer s.Close() c := &gohlslib.Client{ - URI: "http://myuser:mypass@127.0.0.1:8888/mystream/index.m3u8", + URI: "http://myuser:mypass@127.0.0.1:8888/teststream/index.m3u8?param=value", } recv := make(chan struct{}) @@ -271,13 +262,17 @@ func TestServerRead(t *testing.T) { ) require.NoError(t, err) - pm := &dummyPathManager{ - findPathConf: func(req defs.PathFindPathConfReq) (*conf.Path, error) { - require.Equal(t, "mystream", req.AccessRequest.Name) + pm := &test.PathManager{ + FindPathConfImpl: func(req defs.PathFindPathConfReq) (*conf.Path, error) { + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) return &conf.Path{}, nil }, - addReader: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { - require.Equal(t, "mystream", req.AccessRequest.Name) + AddReaderImpl: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "", req.AccessRequest.Query) return &dummyPath{}, str, nil }, } @@ -321,7 +316,7 @@ func TestServerRead(t *testing.T) { } c := &gohlslib.Client{ - URI: "http://myuser:mypass@127.0.0.1:8888/mystream/index.m3u8", + URI: "http://myuser:mypass@127.0.0.1:8888/teststream/index.m3u8?param=value", } recv := make(chan struct{}) @@ -371,8 +366,8 @@ func TestDirectory(t *testing.T) { ) require.NoError(t, err) - pm := &dummyPathManager{ - addReader: func(_ defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + pm := &test.PathManager{ + AddReaderImpl: func(_ defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { return &dummyPath{}, str, nil }, } @@ -403,6 +398,6 @@ func TestDirectory(t *testing.T) { time.Sleep(100 * time.Millisecond) - _, err = os.Stat(filepath.Join(dir, "mydir", "mystream")) + _, err = os.Stat(filepath.Join(dir, "mydir", "teststream")) require.NoError(t, err) } diff --git a/internal/servers/rtmp/server_test.go b/internal/servers/rtmp/server_test.go index c98429ea43a..b6537187706 100644 --- a/internal/servers/rtmp/server_test.go +++ b/internal/servers/rtmp/server_test.go @@ -10,7 +10,6 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" - "github.com/bluenviron/mediamtx/internal/auth" "github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/defs" "github.com/bluenviron/mediamtx/internal/externalcmd" @@ -63,24 +62,6 @@ func (p *dummyPath) RemovePublisher(_ defs.PathRemovePublisherReq) { func (p *dummyPath) RemoveReader(_ defs.PathRemoveReaderReq) { } -type dummyPathManager struct { - path *dummyPath -} - -func (pm *dummyPathManager) AddPublisher(req defs.PathAddPublisherReq) (defs.Path, error) { - if req.AccessRequest.User != "myuser" || req.AccessRequest.Pass != "mypass" { - return nil, &auth.Error{} - } - return pm.path, nil -} - -func (pm *dummyPathManager) AddReader(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { - if req.AccessRequest.User != "myuser" || req.AccessRequest.Pass != "mypass" { - return nil, nil, &auth.Error{} - } - return pm.path, pm.path.stream, nil -} - func TestServerPublish(t *testing.T) { for _, encrypt := range []string{ "plain", @@ -105,7 +86,15 @@ func TestServerPublish(t *testing.T) { streamCreated: make(chan struct{}), } - pathManager := &dummyPathManager{path: path} + pathManager := &test.PathManager{ + AddPublisherImpl: func(req defs.PathAddPublisherReq) (defs.Path, error) { + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "user=myuser&pass=mypass¶m=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) + return path, nil + }, + } s := &Server{ Address: "127.0.0.1:1935", @@ -205,7 +194,7 @@ func TestServerRead(t *testing.T) { } desc := &description.Session{Medias: []*description.Media{test.MediaH264}} - stream, err := stream.New( + str, err := stream.New( 512, 1460, desc, @@ -214,9 +203,17 @@ func TestServerRead(t *testing.T) { ) require.NoError(t, err) - path := &dummyPath{stream: stream} + path := &dummyPath{stream: str} - pathManager := &dummyPathManager{path: path} + pathManager := &test.PathManager{ + AddReaderImpl: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "user=myuser&pass=mypass¶m=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) + return path, path.stream, nil + }, + } s := &Server{ Address: "127.0.0.1:1935", @@ -250,9 +247,9 @@ func TestServerRead(t *testing.T) { defer nconn.Close() go func() { - stream.WaitRunningReader() + str.WaitRunningReader() - stream.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ + str.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ Base: unit.Base{ NTP: time.Time{}, }, @@ -261,7 +258,7 @@ func TestServerRead(t *testing.T) { }, }) - stream.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ + str.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ Base: unit.Base{ NTP: time.Time{}, PTS: 2 * 90000, @@ -271,7 +268,7 @@ func TestServerRead(t *testing.T) { }, }) - stream.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ + str.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ Base: unit.Base{ NTP: time.Time{}, PTS: 3 * 90000, diff --git a/internal/servers/rtsp/conn.go b/internal/servers/rtsp/conn.go index 675208f35df..29923fad64a 100644 --- a/internal/servers/rtsp/conn.go +++ b/internal/servers/rtsp/conn.go @@ -131,16 +131,19 @@ func (c *conn) onDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx, } } + req := defs.PathAccessRequest{ + Name: ctx.Path, + Query: ctx.Query, + IP: c.ip(), + Proto: auth.ProtocolRTSP, + ID: &c.uuid, + RTSPRequest: ctx.Request, + RTSPNonce: c.authNonce, + } + req.FillFromRTSPRequest(ctx.Request) + res := c.pathManager.Describe(defs.PathDescribeReq{ - AccessRequest: defs.PathAccessRequest{ - Name: ctx.Path, - Query: ctx.Query, - IP: c.ip(), - Proto: auth.ProtocolRTSP, - ID: &c.uuid, - RTSPRequest: ctx.Request, - RTSPNonce: c.authNonce, - }, + AccessRequest: req, }) if res.Err != nil { diff --git a/internal/servers/rtsp/server_test.go b/internal/servers/rtsp/server_test.go index c594776a695..ecec3230c0f 100644 --- a/internal/servers/rtsp/server_test.go +++ b/internal/servers/rtsp/server_test.go @@ -5,10 +5,11 @@ import ( "time" "github.com/bluenviron/gortsplib/v4" - "github.com/bluenviron/gortsplib/v4/pkg/auth" + rtspauth "github.com/bluenviron/gortsplib/v4/pkg/auth" "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/format" + "github.com/bluenviron/mediamtx/internal/auth" "github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/defs" "github.com/bluenviron/mediamtx/internal/externalcmd" @@ -63,37 +64,27 @@ func (p *dummyPath) RemovePublisher(_ defs.PathRemovePublisherReq) { func (p *dummyPath) RemoveReader(_ defs.PathRemoveReaderReq) { } -type dummyPathManager struct { - path *dummyPath -} - -func (pm *dummyPathManager) Describe(_ defs.PathDescribeReq) defs.PathDescribeRes { - return defs.PathDescribeRes{ - Path: pm.path, - Stream: pm.path.stream, - Redirect: "", - Err: nil, - } -} - -func (pm *dummyPathManager) AddPublisher(_ defs.PathAddPublisherReq) (defs.Path, error) { - return pm.path, nil -} - -func (pm *dummyPathManager) AddReader(_ defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { - return pm.path, pm.path.stream, nil -} - func TestServerPublish(t *testing.T) { path := &dummyPath{ streamCreated: make(chan struct{}), } - pathManager := &dummyPathManager{path: path} + pathManager := &test.PathManager{ + AddPublisherImpl: func(req defs.PathAddPublisherReq) (defs.Path, error) { + if req.AccessRequest.User == "" && req.AccessRequest.Pass == "" { + return nil, &auth.Error{Message: "", AskCredentials: true} + } + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) + return path, nil + }, + } s := &Server{ Address: "127.0.0.1:8557", - AuthMethods: []auth.ValidateMethod{auth.ValidateMethodBasic}, + AuthMethods: []rtspauth.ValidateMethod{rtspauth.ValidateMethodBasic}, ReadTimeout: conf.Duration(10 * time.Second), WriteTimeout: conf.Duration(10 * time.Second), WriteQueueSize: 512, @@ -172,7 +163,7 @@ func TestServerPublish(t *testing.T) { func TestServerRead(t *testing.T) { desc := &description.Session{Medias: []*description.Media{test.MediaH264}} - stream, err := stream.New( + str, err := stream.New( 512, 1460, desc, @@ -181,13 +172,37 @@ func TestServerRead(t *testing.T) { ) require.NoError(t, err) - path := &dummyPath{stream: stream} - - pathManager := &dummyPathManager{path: path} + path := &dummyPath{stream: str} + + pathManager := &test.PathManager{ + DescribeImpl: func(req defs.PathDescribeReq) defs.PathDescribeRes { + if req.AccessRequest.User == "" && req.AccessRequest.Pass == "" { + return defs.PathDescribeRes{Err: &auth.Error{Message: "", AskCredentials: true}} + } + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) + + return defs.PathDescribeRes{ + Path: path, + Stream: path.stream, + Redirect: "", + Err: nil, + } + }, + AddReaderImpl: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) + return path, path.stream, nil + }, + } s := &Server{ Address: "127.0.0.1:8557", - AuthMethods: []auth.ValidateMethod{auth.ValidateMethodBasic}, + AuthMethods: []rtspauth.ValidateMethod{rtspauth.ValidateMethodBasic}, ReadTimeout: conf.Duration(10 * time.Second), WriteTimeout: conf.Duration(10 * time.Second), WriteQueueSize: 512, @@ -256,7 +271,7 @@ func TestServerRead(t *testing.T) { _, err = reader.Play(nil) require.NoError(t, err) - stream.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ + str.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ Base: unit.Base{ NTP: time.Time{}, }, diff --git a/internal/servers/rtsp/session.go b/internal/servers/rtsp/session.go index 0465dcb8f7b..248efee3743 100644 --- a/internal/servers/rtsp/session.go +++ b/internal/servers/rtsp/session.go @@ -111,18 +111,21 @@ func (s *session) onAnnounce(c *conn, ctx *gortsplib.ServerHandlerOnAnnounceCtx) } } + req := defs.PathAccessRequest{ + Name: ctx.Path, + Query: ctx.Query, + Publish: true, + IP: c.ip(), + Proto: auth.ProtocolRTSP, + ID: &c.uuid, + RTSPRequest: ctx.Request, + RTSPNonce: c.authNonce, + } + req.FillFromRTSPRequest(ctx.Request) + path, err := s.pathManager.AddPublisher(defs.PathAddPublisherReq{ - Author: s, - AccessRequest: defs.PathAccessRequest{ - Name: ctx.Path, - Query: ctx.Query, - Publish: true, - IP: c.ip(), - Proto: auth.ProtocolRTSP, - ID: &c.uuid, - RTSPRequest: ctx.Request, - RTSPNonce: c.authNonce, - }, + Author: s, + AccessRequest: req, }) if err != nil { var terr *auth.Error @@ -182,17 +185,20 @@ func (s *session) onSetup(c *conn, ctx *gortsplib.ServerHandlerOnSetupCtx, } } + req := defs.PathAccessRequest{ + Name: ctx.Path, + Query: ctx.Query, + IP: c.ip(), + Proto: auth.ProtocolRTSP, + ID: &c.uuid, + RTSPRequest: ctx.Request, + RTSPNonce: c.authNonce, + } + req.FillFromRTSPRequest(ctx.Request) + path, stream, err := s.pathManager.AddReader(defs.PathAddReaderReq{ - Author: s, - AccessRequest: defs.PathAccessRequest{ - Name: ctx.Path, - Query: ctx.Query, - IP: c.ip(), - Proto: auth.ProtocolRTSP, - ID: &c.uuid, - RTSPRequest: ctx.Request, - RTSPNonce: c.authNonce, - }, + Author: s, + AccessRequest: req, }) if err != nil { var terr *auth.Error diff --git a/internal/servers/srt/conn.go b/internal/servers/srt/conn.go index 9e169e91963..19867c5d04d 100644 --- a/internal/servers/srt/conn.go +++ b/internal/servers/srt/conn.go @@ -141,13 +141,13 @@ func (c *conn) runPublish(streamID *streamID) error { Author: c, AccessRequest: defs.PathAccessRequest{ Name: streamID.path, + Query: streamID.query, IP: c.ip(), Publish: true, User: streamID.user, Pass: streamID.pass, Proto: auth.ProtocolSRT, ID: &c.uuid, - Query: streamID.query, }, }) if err != nil { @@ -241,12 +241,12 @@ func (c *conn) runRead(streamID *streamID) error { Author: c, AccessRequest: defs.PathAccessRequest{ Name: streamID.path, + Query: streamID.query, IP: c.ip(), User: streamID.user, Pass: streamID.pass, Proto: auth.ProtocolSRT, ID: &c.uuid, - Query: streamID.query, }, }) if err != nil { diff --git a/internal/servers/srt/server_test.go b/internal/servers/srt/server_test.go index 5e05cb14a56..ae3db7722c0 100644 --- a/internal/servers/srt/server_test.go +++ b/internal/servers/srt/server_test.go @@ -7,7 +7,6 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/mediacommon/pkg/formats/mpegts" - "github.com/bluenviron/mediamtx/internal/auth" "github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/defs" "github.com/bluenviron/mediamtx/internal/externalcmd" @@ -60,24 +59,6 @@ func (p *dummyPath) RemovePublisher(_ defs.PathRemovePublisherReq) { func (p *dummyPath) RemoveReader(_ defs.PathRemoveReaderReq) { } -type dummyPathManager struct { - path *dummyPath -} - -func (pm *dummyPathManager) AddPublisher(req defs.PathAddPublisherReq) (defs.Path, error) { - if req.AccessRequest.User != "myuser" || req.AccessRequest.Pass != "mypass" { - return nil, &auth.Error{} - } - return pm.path, nil -} - -func (pm *dummyPathManager) AddReader(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { - if req.AccessRequest.User != "myuser" || req.AccessRequest.Pass != "mypass" { - return nil, nil, &auth.Error{} - } - return pm.path, pm.path.stream, nil -} - func TestServerPublish(t *testing.T) { externalCmdPool := externalcmd.NewPool() defer externalCmdPool.Close() @@ -86,7 +67,15 @@ func TestServerPublish(t *testing.T) { streamCreated: make(chan struct{}), } - pathManager := &dummyPathManager{path: path} + pathManager := &test.PathManager{ + AddPublisherImpl: func(req defs.PathAddPublisherReq) (defs.Path, error) { + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) + return path, nil + }, + } s := &Server{ Address: "127.0.0.1:8890", @@ -105,7 +94,7 @@ func TestServerPublish(t *testing.T) { require.NoError(t, err) defer s.Close() - u := "srt://127.0.0.1:8890?streamid=publish:mypath:myuser:mypass" + u := "srt://127.0.0.1:8890?streamid=publish:teststream:myuser:mypass:param=value" srtConf := srt.DefaultConfig() address, err := srtConf.UnmarshalURL(u) @@ -176,7 +165,7 @@ func TestServerRead(t *testing.T) { desc := &description.Session{Medias: []*description.Media{test.MediaH264}} - stream, err := stream.New( + str, err := stream.New( 512, 1460, desc, @@ -185,9 +174,17 @@ func TestServerRead(t *testing.T) { ) require.NoError(t, err) - path := &dummyPath{stream: stream} + path := &dummyPath{stream: str} - pathManager := &dummyPathManager{path: path} + pathManager := &test.PathManager{ + AddReaderImpl: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) + return path, path.stream, nil + }, + } s := &Server{ Address: "127.0.0.1:8890", @@ -206,7 +203,7 @@ func TestServerRead(t *testing.T) { require.NoError(t, err) defer s.Close() - u := "srt://127.0.0.1:8890?streamid=read:mypath:myuser:mypass" + u := "srt://127.0.0.1:8890?streamid=read:teststream:myuser:mypass:param=value" srtConf := srt.DefaultConfig() address, err := srtConf.UnmarshalURL(u) @@ -219,9 +216,9 @@ func TestServerRead(t *testing.T) { require.NoError(t, err) defer reader.Close() - stream.WaitRunningReader() + str.WaitRunningReader() - stream.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ + str.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ Base: unit.Base{ NTP: time.Time{}, }, @@ -252,7 +249,7 @@ func TestServerRead(t *testing.T) { return nil }) - stream.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ + str.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{ Base: unit.Base{ NTP: time.Time{}, }, diff --git a/internal/servers/webrtc/http_server.go b/internal/servers/webrtc/http_server.go index e2bac5f3639..5d96848d317 100644 --- a/internal/servers/webrtc/http_server.go +++ b/internal/servers/webrtc/http_server.go @@ -124,14 +124,16 @@ func (s *httpServer) close() { } func (s *httpServer) checkAuthOutsideSession(ctx *gin.Context, pathName string, publish bool) bool { + req := defs.PathAccessRequest{ + Name: pathName, + Publish: publish, + IP: net.ParseIP(ctx.ClientIP()), + Proto: auth.ProtocolWebRTC, + } + req.FillFromHTTPRequest(ctx.Request) + _, err := s.pathManager.FindPathConf(defs.PathFindPathConfReq{ - AccessRequest: defs.PathAccessRequest{ - Name: pathName, - Publish: publish, - IP: net.ParseIP(ctx.ClientIP()), - Proto: auth.ProtocolWebRTC, - HTTPRequest: ctx.Request, - }, + AccessRequest: req, }) if err != nil { var terr *auth.Error diff --git a/internal/servers/webrtc/server_test.go b/internal/servers/webrtc/server_test.go index 03d03f829af..e78adac37af 100644 --- a/internal/servers/webrtc/server_test.go +++ b/internal/servers/webrtc/server_test.go @@ -76,27 +76,9 @@ func (p *dummyPath) RemovePublisher(_ defs.PathRemovePublisherReq) { func (p *dummyPath) RemoveReader(_ defs.PathRemoveReaderReq) { } -type dummyPathManager struct { - findPathConf func(req defs.PathFindPathConfReq) (*conf.Path, error) - addPublisher func(req defs.PathAddPublisherReq) (defs.Path, error) - addReader func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) -} - -func (pm *dummyPathManager) FindPathConf(req defs.PathFindPathConfReq) (*conf.Path, error) { - return pm.findPathConf(req) -} - -func (pm *dummyPathManager) AddPublisher(req defs.PathAddPublisherReq) (defs.Path, error) { - return pm.addPublisher(req) -} - -func (pm *dummyPathManager) AddReader(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { - return pm.addReader(req) -} - func initializeTestServer(t *testing.T) *Server { - pm := &dummyPathManager{ - findPathConf: func(_ defs.PathFindPathConfReq) (*conf.Path, error) { + pm := &test.PathManager{ + FindPathConfImpl: func(_ defs.PathFindPathConfReq) (*conf.Path, error) { return &conf.Path{}, nil }, } @@ -179,8 +161,8 @@ func TestPreflightRequest(t *testing.T) { } func TestServerOptionsICEServer(t *testing.T) { - pathManager := &dummyPathManager{ - findPathConf: func(_ defs.PathFindPathConfReq) (*conf.Path, error) { + pathManager := &test.PathManager{ + FindPathConfImpl: func(_ defs.PathFindPathConfReq) (*conf.Path, error) { return &conf.Path{}, nil }, } @@ -242,13 +224,19 @@ func TestServerPublish(t *testing.T) { streamCreated: make(chan struct{}), } - pathManager := &dummyPathManager{ - findPathConf: func(req defs.PathFindPathConfReq) (*conf.Path, error) { + pathManager := &test.PathManager{ + FindPathConfImpl: func(req defs.PathFindPathConfReq) (*conf.Path, error) { require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) return &conf.Path{}, nil }, - addPublisher: func(req defs.PathAddPublisherReq) (defs.Path, error) { + AddPublisherImpl: func(req defs.PathAddPublisherReq) (defs.Path, error) { require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) return path, nil }, } @@ -523,13 +511,19 @@ func TestServerRead(t *testing.T) { path := &dummyPath{stream: str} - pathManager := &dummyPathManager{ - findPathConf: func(req defs.PathFindPathConfReq) (*conf.Path, error) { + pathManager := &test.PathManager{ + FindPathConfImpl: func(req defs.PathFindPathConfReq) (*conf.Path, error) { require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) return &conf.Path{}, nil }, - addReader: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + AddReaderImpl: func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { require.Equal(t, "teststream", req.AccessRequest.Name) + require.Equal(t, "param=value", req.AccessRequest.Query) + require.Equal(t, "myuser", req.AccessRequest.User) + require.Equal(t, "mypass", req.AccessRequest.Pass) return path, str, nil }, } @@ -613,11 +607,11 @@ func TestServerRead(t *testing.T) { } func TestServerReadNotFound(t *testing.T) { - pm := &dummyPathManager{ - findPathConf: func(_ defs.PathFindPathConfReq) (*conf.Path, error) { + pm := &test.PathManager{ + FindPathConfImpl: func(_ defs.PathFindPathConfReq) (*conf.Path, error) { return &conf.Path{}, nil }, - addReader: func(_ defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + AddReaderImpl: func(_ defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { return nil, nil, defs.PathNoOnePublishingError{} }, } diff --git a/internal/servers/webrtc/session.go b/internal/servers/webrtc/session.go index e107c10c8ed..73de3dee269 100644 --- a/internal/servers/webrtc/session.go +++ b/internal/servers/webrtc/session.go @@ -126,16 +126,18 @@ func (s *session) runInner2() (int, error) { func (s *session) runPublish() (int, error) { ip, _, _ := net.SplitHostPort(s.req.remoteAddr) + req := defs.PathAccessRequest{ + Name: s.req.pathName, + Publish: true, + IP: net.ParseIP(ip), + Proto: auth.ProtocolWebRTC, + ID: &s.uuid, + } + req.FillFromHTTPRequest(s.req.httpRequest) + path, err := s.pathManager.AddPublisher(defs.PathAddPublisherReq{ - Author: s, - AccessRequest: defs.PathAccessRequest{ - Name: s.req.pathName, - Publish: true, - IP: net.ParseIP(ip), - Proto: auth.ProtocolWebRTC, - ID: &s.uuid, - HTTPRequest: s.req.httpRequest, - }, + Author: s, + AccessRequest: req, }) if err != nil { return http.StatusBadRequest, err @@ -237,15 +239,17 @@ func (s *session) runPublish() (int, error) { func (s *session) runRead() (int, error) { ip, _, _ := net.SplitHostPort(s.req.remoteAddr) + req := defs.PathAccessRequest{ + Name: s.req.pathName, + IP: net.ParseIP(ip), + Proto: auth.ProtocolWebRTC, + ID: &s.uuid, + } + req.FillFromHTTPRequest(s.req.httpRequest) + path, stream, err := s.pathManager.AddReader(defs.PathAddReaderReq{ - Author: s, - AccessRequest: defs.PathAccessRequest{ - Name: s.req.pathName, - IP: net.ParseIP(ip), - Proto: auth.ProtocolWebRTC, - ID: &s.uuid, - HTTPRequest: s.req.httpRequest, - }, + Author: s, + AccessRequest: req, }) if err != nil { var terr2 defs.PathNoOnePublishingError diff --git a/internal/test/auth_manager.go b/internal/test/auth_manager.go index f59666b78a8..c4f20e3f2f5 100644 --- a/internal/test/auth_manager.go +++ b/internal/test/auth_manager.go @@ -2,7 +2,7 @@ package test import "github.com/bluenviron/mediamtx/internal/auth" -// AuthManager is a test auth manager. +// AuthManager is a dummy auth manager. type AuthManager struct { fnc func(req *auth.Request) error } diff --git a/internal/test/formats.go b/internal/test/formats.go index 83ff40ed726..f88bf3d9a82 100644 --- a/internal/test/formats.go +++ b/internal/test/formats.go @@ -5,7 +5,7 @@ import ( "github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio" ) -// FormatH264 is a test H264 format. +// FormatH264 is a dummy H264 format. var FormatH264 = &format.H264{ PayloadTyp: 96, SPS: []byte{ // 1920x1080 baseline @@ -17,7 +17,7 @@ var FormatH264 = &format.H264{ PacketizationMode: 1, } -// FormatH265 is a test H265 format. +// FormatH265 is a dummy H265 format. var FormatH265 = &format.H265{ PayloadTyp: 96, VPS: []byte{ @@ -40,7 +40,7 @@ var FormatH265 = &format.H265{ }, } -// FormatMPEG4Audio is a test MPEG-4 audio format. +// FormatMPEG4Audio is a dummy MPEG-4 audio format. var FormatMPEG4Audio = &format.MPEG4Audio{ PayloadTyp: 96, Config: &mpeg4audio.Config{ diff --git a/internal/test/logger.go b/internal/test/logger.go index 77c4003c728..4dd3256b61b 100644 --- a/internal/test/logger.go +++ b/internal/test/logger.go @@ -18,7 +18,7 @@ func (l *testLogger) Log(level logger.Level, format string, args ...interface{}) l.cb(level, format, args...) } -// Logger returns a test logger. +// Logger returns a dummy logger. func Logger(cb func(logger.Level, string, ...interface{})) logger.Writer { return &testLogger{cb: cb} } diff --git a/internal/test/medias.go b/internal/test/medias.go index 84b37520aaf..1fa1d79f9c5 100644 --- a/internal/test/medias.go +++ b/internal/test/medias.go @@ -5,13 +5,13 @@ import ( "github.com/bluenviron/gortsplib/v4/pkg/format" ) -// MediaH264 is a test H264 media. +// MediaH264 is a dummy H264 media. var MediaH264 = UniqueMediaH264() -// MediaMPEG4Audio is a test MPEG-4 audio media. +// MediaMPEG4Audio is a dummy MPEG-4 audio media. var MediaMPEG4Audio = UniqueMediaMPEG4Audio() -// UniqueMediaH264 is a test H264 media. +// UniqueMediaH264 is a dummy H264 media. func UniqueMediaH264() *description.Media { return &description.Media{ Type: description.MediaTypeVideo, @@ -19,7 +19,7 @@ func UniqueMediaH264() *description.Media { } } -// UniqueMediaMPEG4Audio is a test MPEG-4 audio media. +// UniqueMediaMPEG4Audio is a dummy MPEG-4 audio media. func UniqueMediaMPEG4Audio() *description.Media { return &description.Media{ Type: description.MediaTypeAudio, diff --git a/internal/test/path_manager.go b/internal/test/path_manager.go new file mode 100644 index 00000000000..fad9da51754 --- /dev/null +++ b/internal/test/path_manager.go @@ -0,0 +1,35 @@ +package test + +import ( + "github.com/bluenviron/mediamtx/internal/conf" + "github.com/bluenviron/mediamtx/internal/defs" + "github.com/bluenviron/mediamtx/internal/stream" +) + +// PathManager is a dummy path manager. +type PathManager struct { + FindPathConfImpl func(req defs.PathFindPathConfReq) (*conf.Path, error) + DescribeImpl func(req defs.PathDescribeReq) defs.PathDescribeRes + AddPublisherImpl func(req defs.PathAddPublisherReq) (defs.Path, error) + AddReaderImpl func(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) +} + +// FindPathConf implements PathManager. +func (pm *PathManager) FindPathConf(req defs.PathFindPathConfReq) (*conf.Path, error) { + return pm.FindPathConfImpl(req) +} + +// Describe implements PathManager. +func (pm *PathManager) Describe(req defs.PathDescribeReq) defs.PathDescribeRes { + return pm.DescribeImpl(req) +} + +// AddPublisher implements PathManager. +func (pm *PathManager) AddPublisher(req defs.PathAddPublisherReq) (defs.Path, error) { + return pm.AddPublisherImpl(req) +} + +// AddReader implements PathManager. +func (pm *PathManager) AddReader(req defs.PathAddReaderReq) (defs.Path, *stream.Stream, error) { + return pm.AddReaderImpl(req) +} diff --git a/internal/test/tls_cert.go b/internal/test/tls_cert.go index a1740dd3304..61f85e35fc0 100644 --- a/internal/test/tls_cert.go +++ b/internal/test/tls_cert.go @@ -1,6 +1,6 @@ package test -// TLSCertPub is the public key of a test certificate. +// TLSCertPub is the public key of a dummy certificate. var TLSCertPub = []byte(`-----BEGIN CERTIFICATE----- MIIDazCCAlOgAwIBAgIUXw1hEC3LFpTsllv7D3ARJyEq7sIwDQYJKoZIhvcNAQEL BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM @@ -24,7 +24,7 @@ XQxaORfgM//NzX9LhUPk -----END CERTIFICATE----- `) -// TLSCertKey is the private key of a test certificate. +// TLSCertKey is the private key of a dummy certificate. var TLSCertKey = []byte(`-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAxvA8skudfNdBrBsIFq+a4ySuzhNZE0y0jRBCmsfAY8zsoms/ KwNiRpdlVBKYtlIw6/Qpu53fmCBFbPdSCATm+yxqjmwYdZBhd7uWmcS5LzSRPh6y @@ -54,7 +54,7 @@ y++U32uuSFiXDcSLarfIsE992MEJLSAynbF1Rsgsr3gXbGiuToJRyxbIeVy7gwzD -----END RSA PRIVATE KEY----- `) -// TLSCertPubAlt is the public key of an alternative test certificate. +// TLSCertPubAlt is the public key of an alternative dummy certificate. var TLSCertPubAlt = []byte(`-----BEGIN CERTIFICATE----- MIIDSTCCAjECFEut6ZxIOnbxi3bhrPLfPQZCLReNMA0GCSqGSIb3DQEBCwUAMGEx CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl @@ -76,7 +76,7 @@ Qx9nosr5fLwhkx46+B/cotsbI/xPDjLF6RQ1OUpcHwg1HI6czoW4hHn33S0zstCf BWt5Q1Mb2tGInbmbUgw3wUu/4nWoY+Mq4DKPlKs= -----END CERTIFICATE-----`) -// TLSCertKeyAlt is the private key of an alternative test certificate. +// TLSCertKeyAlt is the private key of an alternative dummy certificate. var TLSCertKeyAlt = []byte(`-----BEGIN RSA PRIVATE KEY----- MIIEoQIBAAKCAQEAs37xvXi1ykkwwaDPnIFf04YjUSNiWOnaUFfKykBUOW5JA1Az /tgHvyFspYCy15qM7DMI89T7uNmsJhspgbqORw2yOG8TyTUEfaka0dvjL3w4Jmcc