webrtc: return 404 in case a WHIP PATCH or WHIP DELETE request fails (#3232)

This commit is contained in:
Alessandro Ros
2024-04-13 11:04:45 +02:00
committed by GitHub
parent 4354535a94
commit a6cc52f0b2
2 changed files with 159 additions and 58 deletions

View File

@@ -236,7 +236,11 @@ func (s *httpServer) onWHIPPatch(ctx *gin.Context, rawSecret string) {
candidates: candidates, candidates: candidates,
}) })
if res.err != nil { if res.err != nil {
writeError(ctx, http.StatusInternalServerError, res.err) if errors.Is(res.err, ErrSessionNotFound) {
writeError(ctx, http.StatusNotFound, res.err)
} else {
writeError(ctx, http.StatusInternalServerError, res.err)
}
return return
} }
@@ -254,7 +258,11 @@ func (s *httpServer) onWHIPDelete(ctx *gin.Context, rawSecret string) {
secret: secret, secret: secret,
}) })
if err != nil { if err != nil {
writeError(ctx, http.StatusInternalServerError, err) if errors.Is(err, ErrSessionNotFound) {
writeError(ctx, http.StatusNotFound, err)
} else {
writeError(ctx, http.StatusInternalServerError, err)
}
return return
} }

View File

@@ -18,11 +18,16 @@ import (
"github.com/bluenviron/mediamtx/internal/stream" "github.com/bluenviron/mediamtx/internal/stream"
"github.com/bluenviron/mediamtx/internal/test" "github.com/bluenviron/mediamtx/internal/test"
"github.com/bluenviron/mediamtx/internal/unit" "github.com/bluenviron/mediamtx/internal/unit"
"github.com/google/uuid"
"github.com/pion/rtp" "github.com/pion/rtp"
pwebrtc "github.com/pion/webrtc/v3" pwebrtc "github.com/pion/webrtc/v3"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func uint16Ptr(v uint16) *uint16 {
return &v
}
func checkClose(t *testing.T, closeFunc func() error) { func checkClose(t *testing.T, closeFunc func() error) {
require.NoError(t, closeFunc()) require.NoError(t, closeFunc())
} }
@@ -90,7 +95,13 @@ func (pm *dummyPathManager) AddReader(req defs.PathAddReaderReq) (defs.Path, *st
return pm.path, pm.path.stream, nil return pm.path, pm.path.stream, nil
} }
func TestServerStaticPages(t *testing.T) { func initializeTestServer(t *testing.T) *Server {
path := &dummyPath{
streamCreated: make(chan struct{}),
}
pathManager := &dummyPathManager{path: path}
s := &Server{ s := &Server{
Address: "127.0.0.1:8886", Address: "127.0.0.1:8886",
Encryption: false, Encryption: false,
@@ -107,11 +118,17 @@ func TestServerStaticPages(t *testing.T) {
AdditionalHosts: []string{}, AdditionalHosts: []string{},
ICEServers: []conf.WebRTCICEServer{}, ICEServers: []conf.WebRTCICEServer{},
ExternalCmdPool: nil, ExternalCmdPool: nil,
PathManager: &dummyPathManager{}, PathManager: pathManager,
Parent: test.NilLogger{}, Parent: test.NilLogger{},
} }
err := s.Initialize() err := s.Initialize()
require.NoError(t, err) require.NoError(t, err)
return s
}
func TestServerStaticPages(t *testing.T) {
s := initializeTestServer(t)
defer s.Close() defer s.Close()
tr := &http.Transport{} tr := &http.Transport{}
@@ -132,6 +149,75 @@ func TestServerStaticPages(t *testing.T) {
} }
} }
func TestServerOptionsPreflight(t *testing.T) {
s := initializeTestServer(t)
defer s.Close()
tr := &http.Transport{}
defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr}
// preflight requests must always work, without authentication
req, err := http.NewRequest(http.MethodOptions, "http://localhost:8886/teststream/whip", nil)
require.NoError(t, err)
req.Header.Set("Access-Control-Request-Method", "OPTIONS")
res, err := hc.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusNoContent, res.StatusCode)
_, ok := res.Header["Link"]
require.Equal(t, false, ok)
}
func TestServerOptionsICEServer(t *testing.T) {
pathManager := &dummyPathManager{}
s := &Server{
Address: "127.0.0.1:8886",
Encryption: false,
ServerKey: "",
ServerCert: "",
AllowOrigin: "",
TrustedProxies: conf.IPNetworks{},
ReadTimeout: conf.StringDuration(10 * time.Second),
WriteQueueSize: 512,
LocalUDPAddress: "127.0.0.1:8887",
LocalTCPAddress: "127.0.0.1:8887",
IPsFromInterfaces: true,
IPsFromInterfacesList: []string{},
AdditionalHosts: []string{},
ICEServers: []conf.WebRTCICEServer{{
URL: "example.com",
Username: "myuser",
Password: "mypass",
}},
ExternalCmdPool: nil,
PathManager: pathManager,
Parent: test.NilLogger{},
}
err := s.Initialize()
require.NoError(t, err)
defer s.Close()
tr := &http.Transport{}
defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr}
iceServers, err := webrtc.WHIPOptionsICEServers(context.Background(), hc,
"http://myuser:mypass@localhost:8886/nonexisting/whep")
require.NoError(t, err)
require.Equal(t, []pwebrtc.ICEServer{{
URLs: []string{"example.com"},
Username: "myuser",
Credential: "mypass",
}}, iceServers)
}
func TestServerPublish(t *testing.T) { func TestServerPublish(t *testing.T) {
path := &dummyPath{ path := &dummyPath{
streamCreated: make(chan struct{}), streamCreated: make(chan struct{}),
@@ -166,26 +252,7 @@ func TestServerPublish(t *testing.T) {
defer tr.CloseIdleConnections() defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr} hc := &http.Client{Transport: tr}
// preflight requests must always work, without authentication su, err := url.Parse("http://myuser:mypass@localhost:8886/teststream/whip?param=value")
func() {
req, err := http.NewRequest(http.MethodOptions, "http://localhost:8886/teststream/whip", nil)
require.NoError(t, err)
req.Header.Set("Access-Control-Request-Method", "OPTIONS")
res, err := hc.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusNoContent, res.StatusCode)
_, ok := res.Header["Link"]
require.Equal(t, false, ok)
}()
ur := "http://myuser:mypass@localhost:8886/teststream/whip?param=value"
su, err := url.Parse(ur)
require.NoError(t, err) require.NoError(t, err)
wc := &webrtc.WHIPClient{ wc := &webrtc.WHIPClient{
@@ -291,9 +358,7 @@ func TestServerRead(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer s.Close() defer s.Close()
ur := "http://myuser:mypass@localhost:8886/teststream/whep?param=value" u, err := url.Parse("http://myuser:mypass@localhost:8886/teststream/whep?param=value")
u, err := url.Parse(ur)
require.NoError(t, err) require.NoError(t, err)
tr := &http.Transport{} tr := &http.Transport{}
@@ -357,43 +422,15 @@ func TestServerRead(t *testing.T) {
}, pkt) }, pkt)
} }
func TestServerReadNotFound(t *testing.T) { func TestServerPostNotFound(t *testing.T) {
pathManager := &dummyPathManager{} s := initializeTestServer(t)
s := &Server{
Address: "127.0.0.1:8886",
Encryption: false,
ServerKey: "",
ServerCert: "",
AllowOrigin: "",
TrustedProxies: conf.IPNetworks{},
ReadTimeout: conf.StringDuration(10 * time.Second),
WriteQueueSize: 512,
LocalUDPAddress: "127.0.0.1:8887",
LocalTCPAddress: "127.0.0.1:8887",
IPsFromInterfaces: true,
IPsFromInterfacesList: []string{},
AdditionalHosts: []string{},
ICEServers: []conf.WebRTCICEServer{},
ExternalCmdPool: nil,
PathManager: pathManager,
Parent: test.NilLogger{},
}
err := s.Initialize()
require.NoError(t, err)
defer s.Close() defer s.Close()
tr := &http.Transport{} tr := &http.Transport{}
defer tr.CloseIdleConnections() defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr} hc := &http.Client{Transport: tr}
iceServers, err := webrtc.WHIPOptionsICEServers(context.Background(), hc, pc, err := pwebrtc.NewPeerConnection(pwebrtc.Configuration{})
"http://myuser:mypass@localhost:8886/nonexisting/whep")
require.NoError(t, err)
pc, err := pwebrtc.NewPeerConnection(pwebrtc.Configuration{
ICEServers: iceServers,
})
require.NoError(t, err) require.NoError(t, err)
defer pc.Close() //nolint:errcheck defer pc.Close() //nolint:errcheck
@@ -416,6 +453,62 @@ func TestServerReadNotFound(t *testing.T) {
require.Equal(t, http.StatusNotFound, res.StatusCode) require.Equal(t, http.StatusNotFound, res.StatusCode)
} }
func TestServerPatchNotFound(t *testing.T) {
s := initializeTestServer(t)
defer s.Close()
tr := &http.Transport{}
defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr}
pc, err := pwebrtc.NewPeerConnection(pwebrtc.Configuration{})
require.NoError(t, err)
defer pc.Close() //nolint:errcheck
_, err = pc.AddTransceiverFromKind(pwebrtc.RTPCodecTypeVideo)
require.NoError(t, err)
offer, err := pc.CreateOffer(nil)
require.NoError(t, err)
frag, err := webrtc.ICEFragmentMarshal(offer.SDP, []*pwebrtc.ICECandidateInit{{
Candidate: "mycandidate",
SDPMLineIndex: uint16Ptr(0),
}})
require.NoError(t, err)
req, err := http.NewRequest(http.MethodPatch,
"http://localhost:8886/nonexisting/whep/"+uuid.UUID{}.String(), bytes.NewReader(frag))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/trickle-ice-sdpfrag")
res, err := hc.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusNotFound, res.StatusCode)
}
func TestServerDeleteNotFound(t *testing.T) {
s := initializeTestServer(t)
defer s.Close()
tr := &http.Transport{}
defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr}
req, err := http.NewRequest(http.MethodDelete,
"http://localhost:8886/nonexisting/whep/"+uuid.UUID{}.String(), 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)
}
func TestICEServerNoClientOnly(t *testing.T) { func TestICEServerNoClientOnly(t *testing.T) {
s := &Server{ s := &Server{
ICEServers: []conf.WebRTCICEServer{ ICEServers: []conf.WebRTCICEServer{