diff --git a/internal/conf/ips_or_cidrs.go b/internal/conf/ips_or_cidrs.go index a45ef355..bc637630 100644 --- a/internal/conf/ips_or_cidrs.go +++ b/internal/conf/ips_or_cidrs.go @@ -55,3 +55,12 @@ func (d *IPsOrCIDRs) UnmarshalEnv(s string) error { byts, _ := json.Marshal(strings.Split(s, ",")) return d.UnmarshalJSON(byts) } + +// ToTrustedProxies converts IPsOrCIDRs into a string slice for SetTrustedProxies. +func (d *IPsOrCIDRs) ToTrustedProxies() []string { + ret := make([]string, len(*d)) + for i, entry := range *d { + ret[i] = entry.String() + } + return ret +} diff --git a/internal/core/api.go b/internal/core/api.go index 810e9fe8..be828f5d 100644 --- a/internal/core/api.go +++ b/internal/core/api.go @@ -12,6 +12,7 @@ import ( "github.com/google/uuid" "github.com/bluenviron/mediamtx/internal/conf" + "github.com/bluenviron/mediamtx/internal/httpserv" "github.com/bluenviron/mediamtx/internal/logger" ) @@ -195,7 +196,7 @@ type api struct { webRTCManager apiWebRTCManager parent apiParent - httpServer *httpServer + httpServer *httpserv.WrappedServer mutex sync.Mutex } @@ -227,9 +228,9 @@ func newAPI( router := gin.New() router.SetTrustedProxies(nil) - mwLog := httpLoggerMiddleware(a) - router.NoRoute(mwLog, httpServerHeaderMiddleware) - group := router.Group("/", mwLog, httpServerHeaderMiddleware) + mwLog := httpserv.MiddlewareLogger(a) + router.NoRoute(mwLog, httpserv.MiddlewareServerHeader) + group := router.Group("/", mwLog, httpserv.MiddlewareServerHeader) group.GET("/v2/config/get", a.onConfigGet) group.POST("/v2/config/set", a.onConfigSet) @@ -279,8 +280,11 @@ func newAPI( group.POST("/v2/webrtcsessions/kick/:id", a.onWebRTCSessionsKick) } + network, address := restrictNetwork("tcp", address) + var err error - a.httpServer, err = newHTTPServer( + a.httpServer, err = httpserv.NewWrappedServer( + network, address, readTimeout, "", @@ -298,7 +302,7 @@ func newAPI( func (a *api) close() { a.Log(logger.Info, "listener is closing") - a.httpServer.close() + a.httpServer.Close() } func (a *api) Log(level logger.Level, format string, args ...interface{}) { diff --git a/internal/core/hls_http_server.go b/internal/core/hls_http_server.go index 3f487a8b..844b6ce5 100644 --- a/internal/core/hls_http_server.go +++ b/internal/core/hls_http_server.go @@ -12,6 +12,7 @@ import ( "github.com/gin-gonic/gin" "github.com/bluenviron/mediamtx/internal/conf" + "github.com/bluenviron/mediamtx/internal/httpserv" "github.com/bluenviron/mediamtx/internal/logger" ) @@ -32,7 +33,7 @@ type hlsHTTPServer struct { pathManager *pathManager parent hlsHTTPServerParent - inner *httpServer + inner *httpserv.WrappedServer } func newHLSHTTPServer( //nolint:dupl @@ -62,12 +63,15 @@ func newHLSHTTPServer( //nolint:dupl } router := gin.New() - httpSetTrustedProxies(router, trustedProxies) + router.SetTrustedProxies(trustedProxies.ToTrustedProxies()) - router.NoRoute(httpLoggerMiddleware(s), httpServerHeaderMiddleware, s.onRequest) + router.NoRoute(httpserv.MiddlewareLogger(s), httpserv.MiddlewareServerHeader, s.onRequest) + + network, address := restrictNetwork("tcp", address) var err error - s.inner, err = newHTTPServer( + s.inner, err = httpserv.NewWrappedServer( + network, address, readTimeout, serverCert, @@ -86,7 +90,7 @@ func (s *hlsHTTPServer) Log(level logger.Level, format string, args ...interface } func (s *hlsHTTPServer) close() { - s.inner.close() + s.inner.Close() } func (s *hlsHTTPServer) onRequest(ctx *gin.Context) { diff --git a/internal/core/hls_manager.go b/internal/core/hls_manager.go index c15f02a1..0be20123 100644 --- a/internal/core/hls_manager.go +++ b/internal/core/hls_manager.go @@ -131,7 +131,7 @@ func newHLSManager( m.pathManager.hlsManagerSet(m) if m.metrics != nil { - m.metrics.hlsManagerSet(m) + m.metrics.setHLSManager(m) } m.wg.Add(1) @@ -226,7 +226,7 @@ outer: m.pathManager.hlsManagerSet(nil) if m.metrics != nil { - m.metrics.hlsManagerSet(nil) + m.metrics.setHLSManager(nil) } } diff --git a/internal/core/http_server_header.go b/internal/core/http_server_header.go deleted file mode 100644 index 9946df36..00000000 --- a/internal/core/http_server_header.go +++ /dev/null @@ -1,10 +0,0 @@ -package core - -import ( - "github.com/gin-gonic/gin" -) - -func httpServerHeaderMiddleware(ctx *gin.Context) { - ctx.Writer.Header().Set("Server", "mediamtx") - ctx.Next() -} diff --git a/internal/core/http_set_trusted_proxies.go b/internal/core/http_set_trusted_proxies.go deleted file mode 100644 index 9b1986b4..00000000 --- a/internal/core/http_set_trusted_proxies.go +++ /dev/null @@ -1,15 +0,0 @@ -package core - -import ( - "github.com/gin-gonic/gin" - - "github.com/bluenviron/mediamtx/internal/conf" -) - -func httpSetTrustedProxies(router *gin.Engine, trustedProxies conf.IPsOrCIDRs) { - tmp := make([]string, len(trustedProxies)) - for i, entry := range trustedProxies { - tmp[i] = entry.String() - } - router.SetTrustedProxies(tmp) -} diff --git a/internal/core/metrics.go b/internal/core/metrics.go index e310871e..c3cce497 100644 --- a/internal/core/metrics.go +++ b/internal/core/metrics.go @@ -9,6 +9,7 @@ import ( "github.com/gin-gonic/gin" "github.com/bluenviron/mediamtx/internal/conf" + "github.com/bluenviron/mediamtx/internal/httpserv" "github.com/bluenviron/mediamtx/internal/logger" ) @@ -23,7 +24,7 @@ type metricsParent interface { type metrics struct { parent metricsParent - httpServer *httpServer + httpServer *httpserv.WrappedServer mutex sync.Mutex pathManager apiPathManager rtspServer apiRTSPServer @@ -45,12 +46,15 @@ func newMetrics( router := gin.New() router.SetTrustedProxies(nil) - mwLog := httpLoggerMiddleware(m) + mwLog := httpserv.MiddlewareLogger(m) router.NoRoute(mwLog) router.GET("/metrics", mwLog, m.onMetrics) + network, address := restrictNetwork("tcp", address) + var err error - m.httpServer, err = newHTTPServer( + m.httpServer, err = httpserv.NewWrappedServer( + network, address, readTimeout, "", @@ -68,7 +72,7 @@ func newMetrics( func (m *metrics) close() { m.Log(logger.Info, "listener is closing") - m.httpServer.close() + m.httpServer.Close() } func (m *metrics) Log(level logger.Level, format string, args ...interface{}) { @@ -221,8 +225,8 @@ func (m *metrics) pathManagerSet(s apiPathManager) { m.pathManager = s } -// hlsManagerSet is called by hlsManager. -func (m *metrics) hlsManagerSet(s apiHLSManager) { +// setHLSManager is called by hlsManager. +func (m *metrics) setHLSManager(s apiHLSManager) { m.mutex.Lock() defer m.mutex.Unlock() m.hlsManager = s diff --git a/internal/core/pprof.go b/internal/core/pprof.go index ad3e91ea..28701a7a 100644 --- a/internal/core/pprof.go +++ b/internal/core/pprof.go @@ -7,6 +7,7 @@ import ( _ "net/http/pprof" "github.com/bluenviron/mediamtx/internal/conf" + "github.com/bluenviron/mediamtx/internal/httpserv" "github.com/bluenviron/mediamtx/internal/logger" ) @@ -17,7 +18,7 @@ type pprofParent interface { type pprof struct { parent pprofParent - httpServer *httpServer + httpServer *httpserv.WrappedServer } func newPPROF( @@ -29,8 +30,11 @@ func newPPROF( parent: parent, } + network, address := restrictNetwork("tcp", address) + var err error - pp.httpServer, err = newHTTPServer( + pp.httpServer, err = httpserv.NewWrappedServer( + network, address, readTimeout, "", @@ -48,7 +52,7 @@ func newPPROF( func (pp *pprof) close() { pp.Log(logger.Info, "listener is closing") - pp.httpServer.close() + pp.httpServer.Close() } func (pp *pprof) Log(level logger.Level, format string, args ...interface{}) { diff --git a/internal/core/webrtc_http_server.go b/internal/core/webrtc_http_server.go index de67b39a..22086c65 100644 --- a/internal/core/webrtc_http_server.go +++ b/internal/core/webrtc_http_server.go @@ -18,6 +18,7 @@ import ( "github.com/pion/webrtc/v3" "github.com/bluenviron/mediamtx/internal/conf" + "github.com/bluenviron/mediamtx/internal/httpserv" "github.com/bluenviron/mediamtx/internal/logger" ) @@ -170,7 +171,7 @@ type webRTCHTTPServer struct { pathManager *pathManager parent webRTCHTTPServerParent - inner *httpServer + inner *httpserv.WrappedServer } func newWebRTCHTTPServer( //nolint:dupl @@ -200,11 +201,14 @@ func newWebRTCHTTPServer( //nolint:dupl } router := gin.New() - httpSetTrustedProxies(router, trustedProxies) - router.NoRoute(httpLoggerMiddleware(s), httpServerHeaderMiddleware, s.onRequest) + router.SetTrustedProxies(trustedProxies.ToTrustedProxies()) + router.NoRoute(httpserv.MiddlewareLogger(s), httpserv.MiddlewareServerHeader, s.onRequest) + + network, address := restrictNetwork("tcp", address) var err error - s.inner, err = newHTTPServer( + s.inner, err = httpserv.NewWrappedServer( + network, address, readTimeout, serverCert, @@ -223,7 +227,7 @@ func (s *webRTCHTTPServer) Log(level logger.Level, format string, args ...interf } func (s *webRTCHTTPServer) close() { - s.inner.close() + s.inner.Close() } func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) { diff --git a/internal/core/http_logger.go b/internal/httpserv/middleware_logger.go similarity index 72% rename from internal/core/http_logger.go rename to internal/httpserv/middleware_logger.go index 3e6c5ee3..f17f12e4 100644 --- a/internal/core/http_logger.go +++ b/internal/httpserv/middleware_logger.go @@ -1,4 +1,4 @@ -package core +package httpserv import ( "bytes" @@ -11,22 +11,22 @@ import ( "github.com/bluenviron/mediamtx/internal/logger" ) -type httpLoggerWriter struct { +type loggerWriter struct { gin.ResponseWriter buf bytes.Buffer } -func (w *httpLoggerWriter) Write(b []byte) (int, error) { +func (w *loggerWriter) Write(b []byte) (int, error) { w.buf.Write(b) return w.ResponseWriter.Write(b) } -func (w *httpLoggerWriter) WriteString(s string) (int, error) { +func (w *loggerWriter) WriteString(s string) (int, error) { w.buf.WriteString(s) return w.ResponseWriter.WriteString(s) } -func (w *httpLoggerWriter) dump() string { +func (w *loggerWriter) dump() string { var buf bytes.Buffer fmt.Fprintf(&buf, "%s %d %s\n", "HTTP/1.1", w.ResponseWriter.Status(), http.StatusText(w.ResponseWriter.Status())) w.ResponseWriter.Header().Write(&buf) @@ -37,18 +37,15 @@ func (w *httpLoggerWriter) dump() string { return buf.String() } -type httpLoggerParent interface { - logger.Writer -} - -func httpLoggerMiddleware(p httpLoggerParent) func(*gin.Context) { +// MiddlewareLogger is a middleware that logs requests and responses. +func MiddlewareLogger(p logger.Writer) func(*gin.Context) { return func(ctx *gin.Context) { p.Log(logger.Debug, "[conn %v] %s %s", ctx.Request.RemoteAddr, ctx.Request.Method, ctx.Request.URL.Path) byts, _ := httputil.DumpRequest(ctx.Request, true) p.Log(logger.Debug, "[conn %v] [c->s] %s", ctx.Request.RemoteAddr, string(byts)) - logw := &httpLoggerWriter{ResponseWriter: ctx.Writer} + logw := &loggerWriter{ResponseWriter: ctx.Writer} ctx.Writer = logw ctx.Next() diff --git a/internal/httpserv/middleware_server_header.go b/internal/httpserv/middleware_server_header.go new file mode 100644 index 00000000..680402fb --- /dev/null +++ b/internal/httpserv/middleware_server_header.go @@ -0,0 +1,11 @@ +package httpserv + +import ( + "github.com/gin-gonic/gin" +) + +// MiddlewareServerHeader is a middleware that sets the Server header. +func MiddlewareServerHeader(ctx *gin.Context) { + ctx.Writer.Header().Set("Server", "mediamtx") + ctx.Next() +} diff --git a/internal/core/http_server.go b/internal/httpserv/wrapped_server.go similarity index 72% rename from internal/core/http_server.go rename to internal/httpserv/wrapped_server.go index 27b23269..d6108e5c 100644 --- a/internal/core/http_server.go +++ b/internal/httpserv/wrapped_server.go @@ -1,4 +1,5 @@ -package core +// Package httpserv contains HTTP server utilities. +package httpserv import ( "context" @@ -20,7 +21,7 @@ func (nilWriter) Write(p []byte) (int, error) { return len(p), nil } -// exit when there's a panic inside HTTP handlers. +// exit when there's a panic inside the HTTP handler. // https://github.com/golang/go/issues/16542 type exitOnPanicHandler struct { http.Handler @@ -39,19 +40,25 @@ func (h exitOnPanicHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.Handler.ServeHTTP(w, r) } -type httpServer struct { +// WrappedServer is a wrapper around http.Server that provides: +// - net.Listener allocation and closure +// - TLS allocation +// - exit on panic +type WrappedServer struct { ln net.Listener inner *http.Server } -func newHTTPServer( +// NewWrappedServer allocates a WrappedServer. +func NewWrappedServer( + network string, address string, readTimeout conf.StringDuration, serverCert string, serverKey string, handler http.Handler, -) (*httpServer, error) { - ln, err := net.Listen(restrictNetwork("tcp", address)) +) (*WrappedServer, error) { + ln, err := net.Listen(network, address) if err != nil { return nil, err } @@ -69,7 +76,7 @@ func newHTTPServer( } } - s := &httpServer{ + s := &WrappedServer{ ln: ln, inner: &http.Server{ Handler: exitOnPanicHandler{handler}, @@ -88,7 +95,8 @@ func newHTTPServer( return s, nil } -func (s *httpServer) close() { +// Close closes all resources and waits for all routines to return. +func (s *WrappedServer) Close() { s.inner.Shutdown(context.Background()) s.ln.Close() // in case Shutdown() is called before Serve() }