diff --git a/internal/core/api.go b/internal/core/api.go index 65cb81b9..95b8e47e 100644 --- a/internal/core/api.go +++ b/internal/core/api.go @@ -1,15 +1,11 @@ package core import ( - "context" "encoding/json" - "log" - "net" "net/http" "reflect" "strconv" "sync" - "time" "github.com/gin-gonic/gin" "github.com/google/uuid" @@ -169,8 +165,7 @@ type api struct { webRTCManager apiWebRTCManager parent apiParent - ln net.Listener - httpServer *http.Server + httpServer *httpServer mutex sync.Mutex } @@ -187,11 +182,6 @@ func newAPI( webRTCManager apiWebRTCManager, parent apiParent, ) (*api, error) { - ln, err := net.Listen(restrictNetwork("tcp", address)) - if err != nil { - return nil, err - } - a := &api{ conf: conf, pathManager: pathManager, @@ -202,7 +192,6 @@ func newAPI( hlsManager: hlsManager, webRTCManager: webRTCManager, parent: parent, - ln: ln, } router := gin.New() @@ -251,14 +240,18 @@ func newAPI( group.POST("/v1/webrtcsessions/kick/:id", a.onWebRTCSessionsKick) } - a.httpServer = &http.Server{ - Handler: router, - ReadHeaderTimeout: time.Duration(readTimeout), - ErrorLog: log.New(&nilWriter{}, "", 0), + var err error + a.httpServer, err = newHTTPServer( + address, + readTimeout, + "", + "", + router, + ) + if err != nil { + return nil, err } - go a.httpServer.Serve(ln) - a.Log(logger.Info, "listener opened on "+address) return a, nil @@ -266,8 +259,7 @@ func newAPI( func (a *api) close() { a.Log(logger.Info, "listener is closing") - a.httpServer.Shutdown(context.Background()) - a.ln.Close() // in case Shutdown() is called before Serve() + 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 0526283e..b8ecd534 100644 --- a/internal/core/hls_http_server.go +++ b/internal/core/hls_http_server.go @@ -1,15 +1,12 @@ package core import ( - "context" - "crypto/tls" _ "embed" - "log" + "fmt" "net" "net/http" gopath "path" "strings" - "time" "github.com/gin-gonic/gin" @@ -30,8 +27,7 @@ type hlsHTTPServer struct { pathManager *pathManager parent hlsHTTPServerParent - ln net.Listener - inner *http.Server + inner *httpServer } func newHLSHTTPServer( //nolint:dupl @@ -45,29 +41,19 @@ func newHLSHTTPServer( //nolint:dupl pathManager *pathManager, parent hlsHTTPServerParent, ) (*hlsHTTPServer, error) { - ln, err := net.Listen(restrictNetwork("tcp", address)) - if err != nil { - return nil, err - } - - var tlsConfig *tls.Config if encryption { - crt, err := tls.LoadX509KeyPair(serverCert, serverKey) - if err != nil { - ln.Close() - return nil, err - } - - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{crt}, + if serverCert == "" { + return nil, fmt.Errorf("server cert is missing") } + } else { + serverKey = "" + serverCert = "" } s := &hlsHTTPServer{ allowOrigin: allowOrigin, pathManager: pathManager, parent: parent, - ln: ln, } router := gin.New() @@ -75,17 +61,16 @@ func newHLSHTTPServer( //nolint:dupl router.NoRoute(httpLoggerMiddleware(s), httpServerHeaderMiddleware, s.onRequest) - s.inner = &http.Server{ - Handler: router, - TLSConfig: tlsConfig, - ReadHeaderTimeout: time.Duration(readTimeout), - ErrorLog: log.New(&nilWriter{}, "", 0), - } - - if tlsConfig != nil { - go s.inner.ServeTLS(s.ln, "", "") - } else { - go s.inner.Serve(s.ln) + var err error + s.inner, err = newHTTPServer( + address, + readTimeout, + serverCert, + serverKey, + router, + ) + if err != nil { + return nil, err } return s, nil @@ -96,8 +81,7 @@ func (s *hlsHTTPServer) Log(level logger.Level, format string, args ...interface } func (s *hlsHTTPServer) close() { - s.inner.Shutdown(context.Background()) - s.ln.Close() // in case Shutdown() is called before Serve() + 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 e5f110c8..fd13aa76 100644 --- a/internal/core/hls_manager.go +++ b/internal/core/hls_manager.go @@ -10,12 +10,6 @@ import ( "github.com/bluenviron/mediamtx/internal/logger" ) -type nilWriter struct{} - -func (nilWriter) Write(p []byte) (int, error) { - return len(p), nil -} - type hlsManagerAPIMuxersListItem struct { Path string `json:"path"` Created time.Time `json:"created"` diff --git a/internal/core/http_server.go b/internal/core/http_server.go new file mode 100644 index 00000000..75138c1c --- /dev/null +++ b/internal/core/http_server.go @@ -0,0 +1,72 @@ +package core + +import ( + "context" + "crypto/tls" + "log" + "net" + "net/http" + "time" + + "github.com/bluenviron/mediamtx/internal/conf" +) + +type nilWriter struct{} + +func (nilWriter) Write(p []byte) (int, error) { + return len(p), nil +} + +type httpServer struct { + ln net.Listener + inner *http.Server +} + +func newHTTPServer( + address string, + readTimeout conf.StringDuration, + serverCert string, + serverKey string, + handler http.Handler, +) (*httpServer, error) { + ln, err := net.Listen(restrictNetwork("tcp", address)) + if err != nil { + return nil, err + } + + var tlsConfig *tls.Config + if serverCert != "" { + crt, err := tls.LoadX509KeyPair(serverCert, serverKey) + if err != nil { + ln.Close() + return nil, err + } + + tlsConfig = &tls.Config{ + Certificates: []tls.Certificate{crt}, + } + } + + s := &httpServer{ + ln: ln, + inner: &http.Server{ + Handler: handler, + TLSConfig: tlsConfig, + ReadHeaderTimeout: time.Duration(readTimeout), + ErrorLog: log.New(&nilWriter{}, "", 0), + }, + } + + if tlsConfig != nil { + go s.inner.ServeTLS(s.ln, "", "") + } else { + go s.inner.Serve(s.ln) + } + + return s, nil +} + +func (s *httpServer) close() { + s.inner.Shutdown(context.Background()) + s.ln.Close() // in case Shutdown() is called before Serve() +} diff --git a/internal/core/metrics.go b/internal/core/metrics.go index 01558156..be934a48 100644 --- a/internal/core/metrics.go +++ b/internal/core/metrics.go @@ -1,14 +1,10 @@ package core import ( - "context" "io" - "log" - "net" "net/http" "strconv" "sync" - "time" "github.com/gin-gonic/gin" @@ -27,8 +23,7 @@ type metricsParent interface { type metrics struct { parent metricsParent - ln net.Listener - httpServer *http.Server + httpServer *httpServer mutex sync.Mutex pathManager apiPathManager rtspServer apiRTSPServer @@ -43,14 +38,8 @@ func newMetrics( readTimeout conf.StringDuration, parent metricsParent, ) (*metrics, error) { - ln, err := net.Listen(restrictNetwork("tcp", address)) - if err != nil { - return nil, err - } - m := &metrics{ parent: parent, - ln: ln, } router := gin.New() @@ -60,23 +49,26 @@ func newMetrics( router.NoRoute(mwLog) router.GET("/metrics", mwLog, m.onMetrics) - m.httpServer = &http.Server{ - Handler: router, - ReadHeaderTimeout: time.Duration(readTimeout), - ErrorLog: log.New(&nilWriter{}, "", 0), + var err error + m.httpServer, err = newHTTPServer( + address, + readTimeout, + "", + "", + router, + ) + if err != nil { + return nil, err } m.Log(logger.Info, "listener opened on "+address) - go m.httpServer.Serve(m.ln) - return m, nil } func (m *metrics) close() { m.Log(logger.Info, "listener is closing") - m.httpServer.Shutdown(context.Background()) - m.ln.Close() // in case Shutdown() is called before Serve() + m.httpServer.close() } func (m *metrics) Log(level logger.Level, format string, args ...interface{}) { diff --git a/internal/core/pprof.go b/internal/core/pprof.go index 2e8c9c0b..ad3e91ea 100644 --- a/internal/core/pprof.go +++ b/internal/core/pprof.go @@ -1,11 +1,7 @@ package core import ( - "context" - "log" - "net" "net/http" - "time" // start pprof _ "net/http/pprof" @@ -21,8 +17,7 @@ type pprofParent interface { type pprof struct { parent pprofParent - ln net.Listener - httpServer *http.Server + httpServer *httpServer } func newPPROF( @@ -30,33 +25,30 @@ func newPPROF( readTimeout conf.StringDuration, parent pprofParent, ) (*pprof, error) { - ln, err := net.Listen(restrictNetwork("tcp", address)) + pp := &pprof{ + parent: parent, + } + + var err error + pp.httpServer, err = newHTTPServer( + address, + readTimeout, + "", + "", + http.DefaultServeMux, + ) if err != nil { return nil, err } - pp := &pprof{ - parent: parent, - ln: ln, - } - - pp.httpServer = &http.Server{ - Handler: http.DefaultServeMux, - ReadHeaderTimeout: time.Duration(readTimeout), - ErrorLog: log.New(&nilWriter{}, "", 0), - } - pp.Log(logger.Info, "listener opened on "+address) - go pp.httpServer.Serve(pp.ln) - return pp, nil } func (pp *pprof) close() { pp.Log(logger.Info, "listener is closing") - pp.httpServer.Shutdown(context.Background()) - pp.ln.Close() // in case Shutdown() is called before Serve() + 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 f003e64e..8c6cf7aa 100644 --- a/internal/core/webrtc_http_server.go +++ b/internal/core/webrtc_http_server.go @@ -1,17 +1,13 @@ package core import ( - "context" - "crypto/tls" _ "embed" "fmt" "io" - "log" "net" "net/http" "strconv" "strings" - "time" "github.com/gin-gonic/gin" "github.com/google/uuid" @@ -118,8 +114,7 @@ type webRTCHTTPServer struct { pathManager *pathManager parent webRTCHTTPServerParent - ln net.Listener - inner *http.Server + inner *httpServer } func newWebRTCHTTPServer( //nolint:dupl @@ -133,46 +128,35 @@ func newWebRTCHTTPServer( //nolint:dupl pathManager *pathManager, parent webRTCHTTPServerParent, ) (*webRTCHTTPServer, error) { - ln, err := net.Listen(restrictNetwork("tcp", address)) - if err != nil { - return nil, err - } - - var tlsConfig *tls.Config if encryption { - crt, err := tls.LoadX509KeyPair(serverCert, serverKey) - if err != nil { - ln.Close() - return nil, err - } - - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{crt}, + if serverCert == "" { + return nil, fmt.Errorf("server cert is missing") } + } else { + serverKey = "" + serverCert = "" } s := &webRTCHTTPServer{ allowOrigin: allowOrigin, pathManager: pathManager, parent: parent, - ln: ln, } router := gin.New() httpSetTrustedProxies(router, trustedProxies) router.NoRoute(httpLoggerMiddleware(s), httpServerHeaderMiddleware, s.onRequest) - s.inner = &http.Server{ - Handler: router, - TLSConfig: tlsConfig, - ReadHeaderTimeout: time.Duration(readTimeout), - ErrorLog: log.New(&nilWriter{}, "", 0), - } - - if tlsConfig != nil { - go s.inner.ServeTLS(s.ln, "", "") - } else { - go s.inner.Serve(s.ln) + var err error + s.inner, err = newHTTPServer( + address, + readTimeout, + serverCert, + serverKey, + router, + ) + if err != nil { + return nil, err } return s, nil @@ -183,8 +167,7 @@ func (s *webRTCHTTPServer) Log(level logger.Level, format string, args ...interf } func (s *webRTCHTTPServer) close() { - s.inner.Shutdown(context.Background()) - s.ln.Close() // in case Shutdown() is called before Serve() + s.inner.close() } func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) {