From f1617efb0fe4b16fe909e164a4cf1b60e9a832fb Mon Sep 17 00:00:00 2001 From: "Stanislav N. aka pztrn" Date: Sat, 20 Sep 2025 09:59:09 +0500 Subject: [PATCH] HTTP server with WebSockets. --- server/internal/services/core/httpserver.go | 13 +++++++--- .../core/httpserver/default_handler.go | 8 ++++++ .../services/core/httpserver/httpserver.go | 7 ++++++ .../core/httpserver/request_logger.go | 24 ++++++++++++++++++ .../services/core/httpserver/server.go | 25 ++++++++++++++++--- server/internal/services/core/options.go | 2 -- 6 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 server/internal/services/core/httpserver/default_handler.go create mode 100644 server/internal/services/core/httpserver/request_logger.go diff --git a/server/internal/services/core/httpserver.go b/server/internal/services/core/httpserver.go index 42d2c55..1b91ced 100644 --- a/server/internal/services/core/httpserver.go +++ b/server/internal/services/core/httpserver.go @@ -2,6 +2,7 @@ package core import ( "errors" + "net/http" ) // ServiceNameHTTPServer is a name for HTTP server service. @@ -11,6 +12,12 @@ const ServiceNameHTTPServer = "core/http_server" var ErrHTTPServerIsInvalid = errors.New("HTTP server service implementation is invalid") // HTTPServer is an interface for HTTP server service. -// -//nolint:iface -type HTTPServer interface{} +type HTTPServer interface { + // RegisterHandler registers HTTP handler. + RegisterHandler(method, path string, handler http.HandlerFunc) + // RegisterMiddleware registers HTTP server middlewares. + RegisterMiddleware(middleware HTTPMiddlewareFunc) +} + +// HTTPMiddlewareFunc is a function that acts as middleware for HTTP requests. +type HTTPMiddlewareFunc func(fn http.HandlerFunc) http.HandlerFunc diff --git a/server/internal/services/core/httpserver/default_handler.go b/server/internal/services/core/httpserver/default_handler.go new file mode 100644 index 0000000..5563218 --- /dev/null +++ b/server/internal/services/core/httpserver/default_handler.go @@ -0,0 +1,8 @@ +package httpserver + +import "net/http" + +func (h *httpServer) defaultHandler(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNotFound) + _, _ = w.Write([]byte("Unknown path.")) +} diff --git a/server/internal/services/core/httpserver/httpserver.go b/server/internal/services/core/httpserver/httpserver.go index bc4c12c..b6a2775 100644 --- a/server/internal/services/core/httpserver/httpserver.go +++ b/server/internal/services/core/httpserver/httpserver.go @@ -21,6 +21,9 @@ type httpServer struct { logger *slog.Logger db core.Database httpSrv *http.Server + httpMux *http.ServeMux + + middlewares []core.HTTPMiddlewareFunc } // Initialize initializes service. @@ -67,6 +70,10 @@ func (h *httpServer) Initialize() error { h.logger.Info("Initializing...") + h.middlewares = make([]core.HTTPMiddlewareFunc, 0) + + h.RegisterMiddleware(h.requestLoggingMiddleware) + return nil } diff --git a/server/internal/services/core/httpserver/request_logger.go b/server/internal/services/core/httpserver/request_logger.go new file mode 100644 index 0000000..f50b058 --- /dev/null +++ b/server/internal/services/core/httpserver/request_logger.go @@ -0,0 +1,24 @@ +package httpserver + +import ( + "fmt" + "net/http" + "time" +) + +func (h *httpServer) requestLoggingMiddleware(fn http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + startTime := time.Now() + + fn(w, r) + + h.logger.Info( + "HTTP request.", + "remote_addr", r.RemoteAddr, + "user_agent", r.UserAgent(), + "host", r.Host, + "path", fmt.Sprintf("%s %s", r.Method, r.RequestURI), + "duration", time.Since(startTime), + ) + } +} diff --git a/server/internal/services/core/httpserver/server.go b/server/internal/services/core/httpserver/server.go index befffe8..bbded5c 100644 --- a/server/internal/services/core/httpserver/server.go +++ b/server/internal/services/core/httpserver/server.go @@ -9,6 +9,8 @@ import ( "os" "time" + "bunker/server/internal/services/core" + "github.com/coder/websocket" ) @@ -35,12 +37,15 @@ func (h *httpServer) configureHTTPServer() error { return fmt.Errorf("configure HTTP server: validate HTTP server address: %w", errHTTPServerAddrInvalid) } - mux := new(http.ServeMux) - mux.HandleFunc("GET /api/v1/socket", h.handleWebsocketRequest) + h.httpMux = new(http.ServeMux) + + // Default catch-all handler. + h.RegisterHandler("", "/", h.defaultHandler) + h.RegisterHandler(http.MethodGet, "/api/v1/socket", h.handleWebsocketRequest) h.httpSrv = &http.Server{ Addr: httpSrvAddr, - Handler: mux, + Handler: h.httpMux, ReadHeaderTimeout: time.Second * 3, } @@ -66,6 +71,20 @@ func (h *httpServer) handleWebsocketRequest(w http.ResponseWriter, r *http.Reque }() } +func (h *httpServer) RegisterHandler(method, path string, handler http.HandlerFunc) { + h.httpMux.HandleFunc(fmt.Sprintf("%s %s", method, path), func(w http.ResponseWriter, r *http.Request) { + for i := len(h.middlewares) - 1; i >= 0; i-- { + handler = h.middlewares[i](handler) + } + + handler(w, r) + }) +} + +func (h *httpServer) RegisterMiddleware(middleware core.HTTPMiddlewareFunc) { + h.middlewares = append(h.middlewares, middleware) +} + func (h *httpServer) startHTTPServer() { h.logger.Info("Starting listening for HTTP requests.", "address", h.httpSrv.Addr) diff --git a/server/internal/services/core/options.go b/server/internal/services/core/options.go index 67ce4ad..80198ff 100644 --- a/server/internal/services/core/options.go +++ b/server/internal/services/core/options.go @@ -11,6 +11,4 @@ const ServiceNameOptions = "core/options" var ErrOptionsIsInvalid = errors.New("options service implementation is invalid") // Options is an interface for options service. -// -//nolint:iface type Options interface{}