1
0
Fork 0
forked from External/mediamtx

fix support for HTTP preflight requests (#1792) (#3535)

This commit is contained in:
Alessandro Ros 2024-07-06 21:45:15 +02:00 committed by GitHub
parent 342c257df5
commit 3f1d182d2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 273 additions and 10 deletions

View file

@ -251,6 +251,15 @@ func (a *API) writeError(ctx *gin.Context, status int, err error) {
func (a *API) middlewareOrigin(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", a.AllowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
// preflight requests
if ctx.Request.Method == http.MethodOptions &&
ctx.Request.Header.Get("Access-Control-Request-Method") != "" {
ctx.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PATCH, DELETE")
ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
ctx.AbortWithStatus(http.StatusNoContent)
return
}
}
func (a *API) middlewareAuth(ctx *gin.Context) {

View file

@ -74,6 +74,43 @@ func checkError(t *testing.T, msg string, body io.Reader) {
require.Equal(t, map[string]interface{}{"error": msg}, resErr)
}
func TestPreflightRequest(t *testing.T) {
api := API{
Address: "localhost:9997",
AllowOrigin: "*",
ReadTimeout: conf.StringDuration(10 * time.Second),
AuthManager: test.NilAuthManager,
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.MethodOptions, "http://localhost:9997", nil)
require.NoError(t, err)
req.Header.Add("Access-Control-Request-Method", "GET")
res, err := hc.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusNoContent, res.StatusCode)
byts, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, "*", res.Header.Get("Access-Control-Allow-Origin"))
require.Equal(t, "true", res.Header.Get("Access-Control-Allow-Credentials"))
require.Equal(t, "OPTIONS, GET, POST, PATCH, DELETE", res.Header.Get("Access-Control-Allow-Methods"))
require.Equal(t, "Authorization, Content-Type", res.Header.Get("Access-Control-Allow-Headers"))
require.Equal(t, byts, []byte{})
}
func TestConfigAuth(t *testing.T) {
cnf := tempConf(t, "api: yes\n")

View file

@ -107,6 +107,15 @@ func (m *Metrics) onRequest(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", m.AllowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
// preflight requests
if ctx.Request.Method == http.MethodOptions &&
ctx.Request.Header.Get("Access-Control-Request-Method") != "" {
ctx.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET")
ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization")
ctx.Writer.WriteHeader(http.StatusNoContent)
return
}
if ctx.Request.URL.Path != "/metrics" || ctx.Request.Method != http.MethodGet {
return
}

View file

@ -0,0 +1,49 @@
package metrics
import (
"io"
"net/http"
"testing"
"time"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/test"
"github.com/stretchr/testify/require"
)
func TestPreflightRequest(t *testing.T) {
api := Metrics{
Address: "localhost:9998",
AllowOrigin: "*",
ReadTimeout: conf.StringDuration(10 * time.Second),
AuthManager: test.NilAuthManager,
Parent: test.NilLogger,
}
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.MethodOptions, "http://localhost:9998", nil)
require.NoError(t, err)
req.Header.Add("Access-Control-Request-Method", "GET")
res, err := hc.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusNoContent, res.StatusCode)
byts, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, "*", res.Header.Get("Access-Control-Allow-Origin"))
require.Equal(t, "true", res.Header.Get("Access-Control-Allow-Credentials"))
require.Equal(t, "OPTIONS, GET", res.Header.Get("Access-Control-Allow-Methods"))
require.Equal(t, "Authorization", res.Header.Get("Access-Control-Allow-Headers"))
require.Equal(t, byts, []byte{})
}

View file

@ -109,6 +109,15 @@ func (s *Server) safeFindPathConf(name string) (*conf.Path, error) {
func (s *Server) middlewareOrigin(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", s.AllowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
// preflight requests
if ctx.Request.Method == http.MethodOptions &&
ctx.Request.Header.Get("Access-Control-Request-Method") != "" {
ctx.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET")
ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization")
ctx.AbortWithStatus(http.StatusNoContent)
return
}
}
func (s *Server) doAuth(ctx *gin.Context, pathName string) bool {

View file

@ -0,0 +1,48 @@
package playback
import (
"io"
"net/http"
"testing"
"time"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/test"
"github.com/stretchr/testify/require"
)
func TestPreflightRequest(t *testing.T) {
s := &Server{
Address: "127.0.0.1:9996",
AllowOrigin: "*",
ReadTimeout: conf.StringDuration(10 * time.Second),
Parent: test.NilLogger,
}
err := s.Initialize()
require.NoError(t, err)
defer s.Close()
tr := &http.Transport{}
defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr}
req, err := http.NewRequest(http.MethodOptions, "http://localhost:9996", nil)
require.NoError(t, err)
req.Header.Add("Access-Control-Request-Method", "GET")
res, err := hc.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusNoContent, res.StatusCode)
byts, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, "*", res.Header.Get("Access-Control-Allow-Origin"))
require.Equal(t, "true", res.Header.Get("Access-Control-Allow-Credentials"))
require.Equal(t, "OPTIONS, GET", res.Header.Get("Access-Control-Allow-Methods"))
require.Equal(t, "Authorization", res.Header.Get("Access-Control-Allow-Headers"))
require.Equal(t, byts, []byte{})
}

View file

@ -83,6 +83,15 @@ func (pp *PPROF) onRequest(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", pp.AllowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
// preflight requests
if ctx.Request.Method == http.MethodOptions &&
ctx.Request.Header.Get("Access-Control-Request-Method") != "" {
ctx.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET")
ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization")
ctx.Writer.WriteHeader(http.StatusNoContent)
return
}
user, pass, hasCredentials := ctx.Request.BasicAuth()
err := pp.AuthManager.Authenticate(&auth.Request{

View file

@ -0,0 +1,48 @@
package pprof
import (
"io"
"net/http"
"testing"
"time"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/test"
"github.com/stretchr/testify/require"
)
func TestPreflightRequest(t *testing.T) {
s := &PPROF{
Address: "127.0.0.1:9999",
AllowOrigin: "*",
ReadTimeout: conf.StringDuration(10 * time.Second),
Parent: test.NilLogger,
}
err := s.Initialize()
require.NoError(t, err)
defer s.Close()
tr := &http.Transport{}
defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr}
req, err := http.NewRequest(http.MethodOptions, "http://localhost:9999", nil)
require.NoError(t, err)
req.Header.Add("Access-Control-Request-Method", "GET")
res, err := hc.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusNoContent, res.StatusCode)
byts, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, "*", res.Header.Get("Access-Control-Allow-Origin"))
require.Equal(t, "true", res.Header.Get("Access-Control-Allow-Credentials"))
require.Equal(t, "OPTIONS, GET", res.Header.Get("Access-Control-Allow-Methods"))
require.Equal(t, "Authorization", res.Header.Get("Access-Control-Allow-Headers"))
require.Equal(t, byts, []byte{})
}

View file

@ -102,9 +102,11 @@ func (s *httpServer) onRequest(ctx *gin.Context) {
switch ctx.Request.Method {
case http.MethodOptions:
if ctx.Request.Header.Get("Access-Control-Request-Method") != "" {
ctx.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET")
ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Range")
ctx.Writer.WriteHeader(http.StatusNoContent)
}
return
case http.MethodGet:

View file

@ -2,6 +2,7 @@ package hls
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
@ -60,6 +61,42 @@ func (pm *dummyPathManager) AddReader(req defs.PathAddReaderReq) (defs.Path, *st
return pm.addReader(req)
}
func TestPreflightRequest(t *testing.T) {
s := &Server{
Address: "127.0.0.1:8888",
AllowOrigin: "*",
ReadTimeout: conf.StringDuration(10 * time.Second),
Parent: test.NilLogger,
}
err := s.Initialize()
require.NoError(t, err)
defer s.Close()
tr := &http.Transport{}
defer tr.CloseIdleConnections()
hc := &http.Client{Transport: tr}
req, err := http.NewRequest(http.MethodOptions, "http://localhost:8888", nil)
require.NoError(t, err)
req.Header.Add("Access-Control-Request-Method", "GET")
res, err := hc.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusNoContent, res.StatusCode)
byts, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, "*", res.Header.Get("Access-Control-Allow-Origin"))
require.Equal(t, "true", res.Header.Get("Access-Control-Allow-Credentials"))
require.Equal(t, "OPTIONS, GET", res.Header.Get("Access-Control-Allow-Methods"))
require.Equal(t, "Authorization, Range", res.Header.Get("Access-Control-Allow-Headers"))
require.Equal(t, byts, []byte{})
}
func TestServerNotFound(t *testing.T) {
for _, ca := range []string{
"always remux off",

View file

@ -3,6 +3,7 @@ package webrtc
import (
"bytes"
"context"
"io"
"net/http"
"net/url"
"reflect"
@ -102,7 +103,7 @@ func initializeTestServer(t *testing.T) *Server {
Encryption: false,
ServerKey: "",
ServerCert: "",
AllowOrigin: "",
AllowOrigin: "*",
TrustedProxies: conf.IPNetworks{},
ReadTimeout: conf.StringDuration(10 * time.Second),
WriteQueueSize: 512,
@ -146,7 +147,7 @@ func TestServerStaticPages(t *testing.T) {
}
}
func TestServerOptionsPreflight(t *testing.T) {
func TestPreflightRequest(t *testing.T) {
s := initializeTestServer(t)
defer s.Close()
@ -154,11 +155,10 @@ func TestServerOptionsPreflight(t *testing.T) {
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)
req, err := http.NewRequest(http.MethodOptions, "http://localhost:8886", nil)
require.NoError(t, err)
req.Header.Set("Access-Control-Request-Method", "OPTIONS")
req.Header.Add("Access-Control-Request-Method", "GET")
res, err := hc.Do(req)
require.NoError(t, err)
@ -166,8 +166,14 @@ func TestServerOptionsPreflight(t *testing.T) {
require.Equal(t, http.StatusNoContent, res.StatusCode)
_, ok := res.Header["Link"]
require.Equal(t, false, ok)
byts, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, "*", res.Header.Get("Access-Control-Allow-Origin"))
require.Equal(t, "true", res.Header.Get("Access-Control-Allow-Credentials"))
require.Equal(t, "OPTIONS, GET, POST, PATCH, DELETE", res.Header.Get("Access-Control-Allow-Methods"))
require.Equal(t, "Authorization, Content-Type, If-Match", res.Header.Get("Access-Control-Allow-Headers"))
require.Equal(t, byts, []byte{})
}
func TestServerOptionsICEServer(t *testing.T) {