sessions are now tracked through cookies or query parameters. This provides the ability to inspect sessions through logs, metrics and API, allows more precise tracking of outbound bytes, decreases load on external HTTP authentication URLs since they are now called once per session and not once per request.
337 lines
9.0 KiB
Go
337 lines
9.0 KiB
Go
package api //nolint:revive
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/conf"
|
|
"github.com/bluenviron/mediamtx/internal/defs"
|
|
"github.com/bluenviron/mediamtx/internal/servers/hls"
|
|
"github.com/bluenviron/mediamtx/internal/test"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type testHLSServer struct {
|
|
muxers map[string]*defs.APIHLSMuxer
|
|
sessions map[string]*defs.APIHLSSession
|
|
}
|
|
|
|
var testTime = time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC)
|
|
|
|
func (s *testHLSServer) APIMuxersList() (*defs.APIHLSMuxerList, error) {
|
|
items := make([]defs.APIHLSMuxer, 0, len(s.muxers))
|
|
for _, muxer := range s.muxers {
|
|
items = append(items, *muxer)
|
|
}
|
|
return &defs.APIHLSMuxerList{Items: items}, nil
|
|
}
|
|
|
|
func (s *testHLSServer) APIMuxersGet(name string) (*defs.APIHLSMuxer, error) {
|
|
muxer, ok := s.muxers[name]
|
|
if !ok {
|
|
return nil, hls.ErrMuxerNotFound
|
|
}
|
|
return muxer, nil
|
|
}
|
|
|
|
func (s *testHLSServer) APISessionsList() (*defs.APIHLSSessionList, error) {
|
|
items := make([]defs.APIHLSSession, 0, len(s.sessions))
|
|
for _, session := range s.sessions {
|
|
items = append(items, *session)
|
|
}
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].Created.Before(items[j].Created)
|
|
})
|
|
return &defs.APIHLSSessionList{Items: items}, nil
|
|
}
|
|
|
|
func (s *testHLSServer) APISessionsGet(id uuid.UUID) (*defs.APIHLSSession, error) {
|
|
session, ok := s.sessions[id.String()]
|
|
if !ok {
|
|
return nil, hls.ErrSessionNotFound
|
|
}
|
|
return session, nil
|
|
}
|
|
|
|
func (s *testHLSServer) APISessionsKick(id uuid.UUID) error {
|
|
_, ok := s.sessions[id.String()]
|
|
if !ok {
|
|
return hls.ErrSessionNotFound
|
|
}
|
|
delete(s.sessions, id.String())
|
|
return nil
|
|
}
|
|
|
|
func TestHLSMuxersList(t *testing.T) {
|
|
now := testTime
|
|
hlsServer := &testHLSServer{
|
|
muxers: map[string]*defs.APIHLSMuxer{
|
|
"test1": {
|
|
Path: "test1",
|
|
Created: now,
|
|
LastRequest: now.Add(5 * time.Second),
|
|
OutboundBytes: 1234,
|
|
OutboundFramesDiscarded: 10,
|
|
BytesSent: 1234,
|
|
},
|
|
"test2": {
|
|
Path: "test2",
|
|
Created: now.Add(time.Minute),
|
|
LastRequest: now.Add(time.Minute + 10*time.Second),
|
|
OutboundBytes: 5678,
|
|
OutboundFramesDiscarded: 20,
|
|
BytesSent: 5678,
|
|
},
|
|
},
|
|
}
|
|
|
|
api := API{
|
|
Address: "localhost:9997",
|
|
ReadTimeout: conf.Duration(10 * time.Second),
|
|
WriteTimeout: conf.Duration(10 * time.Second),
|
|
AuthManager: test.NilAuthManager,
|
|
HLSServer: hlsServer,
|
|
Parent: &testParent{},
|
|
}
|
|
err := api.Initialize()
|
|
require.NoError(t, err)
|
|
defer api.Close()
|
|
|
|
tr := &http.Transport{}
|
|
defer tr.CloseIdleConnections()
|
|
hc := &http.Client{Transport: tr}
|
|
|
|
var out defs.APIHLSMuxerList
|
|
httpRequest(t, hc, http.MethodGet, "http://localhost:9997/v3/hlsmuxers/list", nil, &out)
|
|
|
|
require.Equal(t, 2, out.ItemCount)
|
|
require.Equal(t, 1, out.PageCount)
|
|
require.Len(t, out.Items, 2)
|
|
}
|
|
|
|
func TestHLSMuxersGet(t *testing.T) {
|
|
now := testTime
|
|
hlsServer := &testHLSServer{
|
|
muxers: map[string]*defs.APIHLSMuxer{
|
|
"mypath": {
|
|
Path: "mypath",
|
|
Created: now,
|
|
LastRequest: now.Add(5 * time.Second),
|
|
OutboundBytes: 9999,
|
|
OutboundFramesDiscarded: 12,
|
|
BytesSent: 9999,
|
|
},
|
|
},
|
|
}
|
|
|
|
api := API{
|
|
Address: "localhost:9997",
|
|
ReadTimeout: conf.Duration(10 * time.Second),
|
|
WriteTimeout: conf.Duration(10 * time.Second),
|
|
AuthManager: test.NilAuthManager,
|
|
HLSServer: hlsServer,
|
|
Parent: &testParent{},
|
|
}
|
|
err := api.Initialize()
|
|
require.NoError(t, err)
|
|
defer api.Close()
|
|
|
|
tr := &http.Transport{}
|
|
defer tr.CloseIdleConnections()
|
|
hc := &http.Client{Transport: tr}
|
|
|
|
var out defs.APIHLSMuxer
|
|
httpRequest(t, hc, http.MethodGet, "http://localhost:9997/v3/hlsmuxers/get/mypath", nil, &out)
|
|
|
|
require.Equal(t, "mypath", out.Path)
|
|
require.Equal(t, uint64(9999), out.OutboundBytes)
|
|
require.Equal(t, uint64(12), out.OutboundFramesDiscarded)
|
|
require.Equal(t, uint64(9999), out.BytesSent)
|
|
}
|
|
|
|
func TestHLSSessionsList(t *testing.T) {
|
|
now := testTime
|
|
hlsServer := &testHLSServer{
|
|
sessions: map[string]*defs.APIHLSSession{
|
|
"session1": {
|
|
ID: uuid.MustParse("18294761-f9d1-4ea9-9a35-fe265b62eb41"),
|
|
Created: now,
|
|
RemoteAddr: "192.168.1.1:5000",
|
|
Path: "stream1",
|
|
Query: "key=val1",
|
|
User: "user1",
|
|
OutboundBytes: 111,
|
|
},
|
|
"session2": {
|
|
ID: uuid.MustParse("18294761-f9d1-4ea9-9a35-fe265b62eb42"),
|
|
Created: now.Add(time.Minute),
|
|
RemoteAddr: "192.168.1.2:5001",
|
|
Path: "stream2",
|
|
Query: "key=val2",
|
|
User: "user2",
|
|
OutboundBytes: 222,
|
|
},
|
|
},
|
|
}
|
|
|
|
api := API{
|
|
Address: "localhost:9997",
|
|
ReadTimeout: conf.Duration(10 * time.Second),
|
|
WriteTimeout: conf.Duration(10 * time.Second),
|
|
AuthManager: test.NilAuthManager,
|
|
HLSServer: hlsServer,
|
|
Parent: &testParent{},
|
|
}
|
|
err := api.Initialize()
|
|
require.NoError(t, err)
|
|
defer api.Close()
|
|
|
|
tr := &http.Transport{}
|
|
defer tr.CloseIdleConnections()
|
|
hc := &http.Client{Transport: tr}
|
|
|
|
var out defs.APIHLSSessionList
|
|
httpRequest(t, hc, http.MethodGet, "http://localhost:9997/v3/hlssessions/list", nil, &out)
|
|
|
|
require.Equal(t, 2, out.ItemCount)
|
|
require.Equal(t, 1, out.PageCount)
|
|
require.Len(t, out.Items, 2)
|
|
require.Equal(t, []defs.APIHLSSession{
|
|
{
|
|
ID: uuid.MustParse("18294761-f9d1-4ea9-9a35-fe265b62eb41"),
|
|
Created: now,
|
|
RemoteAddr: "192.168.1.1:5000",
|
|
Path: "stream1",
|
|
Query: "key=val1",
|
|
User: "user1",
|
|
OutboundBytes: 111,
|
|
},
|
|
{
|
|
ID: uuid.MustParse("18294761-f9d1-4ea9-9a35-fe265b62eb42"),
|
|
Created: now.Add(time.Minute),
|
|
RemoteAddr: "192.168.1.2:5001",
|
|
Path: "stream2",
|
|
Query: "key=val2",
|
|
User: "user2",
|
|
OutboundBytes: 222,
|
|
},
|
|
}, out.Items)
|
|
}
|
|
|
|
func TestHLSSessionsGet(t *testing.T) {
|
|
now := testTime
|
|
hlsServer := &testHLSServer{
|
|
sessions: map[string]*defs.APIHLSSession{
|
|
"18294761-f9d1-4ea9-9a35-fe265b62eb41": {
|
|
ID: uuid.MustParse("18294761-f9d1-4ea9-9a35-fe265b62eb41"),
|
|
Created: now,
|
|
RemoteAddr: "192.168.1.100:5000",
|
|
Path: "mystream",
|
|
Query: "key=val",
|
|
User: "myuser",
|
|
OutboundBytes: 345,
|
|
},
|
|
},
|
|
}
|
|
|
|
api := API{
|
|
Address: "localhost:9997",
|
|
ReadTimeout: conf.Duration(10 * time.Second),
|
|
WriteTimeout: conf.Duration(10 * time.Second),
|
|
AuthManager: test.NilAuthManager,
|
|
HLSServer: hlsServer,
|
|
Parent: &testParent{},
|
|
}
|
|
err := api.Initialize()
|
|
require.NoError(t, err)
|
|
defer api.Close()
|
|
|
|
tr := &http.Transport{}
|
|
defer tr.CloseIdleConnections()
|
|
hc := &http.Client{Transport: tr}
|
|
|
|
sessionID := "18294761-f9d1-4ea9-9a35-fe265b62eb41"
|
|
|
|
var out defs.APIHLSSession
|
|
httpRequest(t, hc, http.MethodGet, fmt.Sprintf("http://localhost:9997/v3/hlssessions/get/%s", sessionID), nil, &out)
|
|
|
|
require.Equal(t, uuid.MustParse(sessionID), out.ID)
|
|
require.Equal(t, "192.168.1.100:5000", out.RemoteAddr)
|
|
require.Equal(t, "mystream", out.Path)
|
|
require.Equal(t, "key=val", out.Query)
|
|
require.Equal(t, "myuser", out.User)
|
|
require.Equal(t, uint64(345), out.OutboundBytes)
|
|
}
|
|
|
|
func TestHLSSessionsKick(t *testing.T) {
|
|
now := testTime
|
|
sessionID := uuid.MustParse("18294761-f9d1-4ea9-9a35-fe265b62eb41")
|
|
hlsServer := &testHLSServer{
|
|
sessions: map[string]*defs.APIHLSSession{
|
|
sessionID.String(): {
|
|
ID: sessionID,
|
|
Created: now,
|
|
RemoteAddr: "192.168.1.100:5000",
|
|
Path: "mystream",
|
|
OutboundBytes: 345,
|
|
},
|
|
},
|
|
}
|
|
|
|
api := API{
|
|
Address: "localhost:9997",
|
|
ReadTimeout: conf.Duration(10 * time.Second),
|
|
WriteTimeout: conf.Duration(10 * time.Second),
|
|
AuthManager: test.NilAuthManager,
|
|
HLSServer: hlsServer,
|
|
Parent: &testParent{},
|
|
}
|
|
err := api.Initialize()
|
|
require.NoError(t, err)
|
|
defer api.Close()
|
|
|
|
tr := &http.Transport{}
|
|
defer tr.CloseIdleConnections()
|
|
hc := &http.Client{Transport: tr}
|
|
|
|
httpRequest(t, hc, http.MethodPost, fmt.Sprintf("http://localhost:9997/v3/hlssessions/kick/%s", sessionID), nil, nil)
|
|
|
|
_, ok := hlsServer.sessions[sessionID.String()]
|
|
require.False(t, ok)
|
|
}
|
|
|
|
func TestHLSSessionsKickNotFound(t *testing.T) {
|
|
hlsServer := &testHLSServer{}
|
|
|
|
api := API{
|
|
Address: "localhost:9997",
|
|
ReadTimeout: conf.Duration(10 * time.Second),
|
|
WriteTimeout: conf.Duration(10 * time.Second),
|
|
AuthManager: test.NilAuthManager,
|
|
HLSServer: hlsServer,
|
|
Parent: &testParent{},
|
|
}
|
|
err := api.Initialize()
|
|
require.NoError(t, err)
|
|
defer api.Close()
|
|
|
|
tr := &http.Transport{}
|
|
defer tr.CloseIdleConnections()
|
|
hc := &http.Client{Transport: tr}
|
|
|
|
req, err := http.NewRequest(http.MethodPost,
|
|
fmt.Sprintf("http://localhost:9997/v3/hlssessions/kick/%s", uuid.New()), nil)
|
|
require.NoError(t, err)
|
|
|
|
res, err := hc.Do(req)
|
|
require.NoError(t, err)
|
|
defer res.Body.Close()
|
|
|
|
require.Equal(t, http.StatusNotFound, res.StatusCode)
|
|
checkError(t, res.Body, "session not found")
|
|
}
|