2025-09-15 09:33:25 +05:00
|
|
|
package httpserver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
2025-09-20 09:59:09 +05:00
|
|
|
"bunker/server/internal/services/core"
|
|
|
|
|
2025-09-15 09:33:25 +05:00
|
|
|
"github.com/coder/websocket"
|
|
|
|
)
|
|
|
|
|
|
|
|
const httpServerAddrEnvVar = "BUNKERD_HTTP_ADDRESS"
|
|
|
|
|
|
|
|
var (
|
|
|
|
errHTTPServerAddrInvalid = errors.New("BUNKERD_HTTP_ADDRESS environment variable contains invalid address to " +
|
|
|
|
"listen, should be 'host:port'")
|
|
|
|
errHTTPServerAddrNotFound = errors.New("BUNKERD_HTTP_ADDRESS environment variable empty")
|
|
|
|
)
|
|
|
|
|
|
|
|
func (h *httpServer) configureHTTPServer() error {
|
|
|
|
httpSrvAddr, found := os.LookupEnv(httpServerAddrEnvVar)
|
|
|
|
if !found {
|
|
|
|
return fmt.Errorf("configure HTTP server: get address from environment variable: %w", errHTTPServerAddrNotFound)
|
|
|
|
}
|
|
|
|
|
|
|
|
host, port, err := net.SplitHostPort(httpSrvAddr)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("configure HTTP server: validate HTTP server address: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if httpSrvAddr != host+":"+port {
|
|
|
|
return fmt.Errorf("configure HTTP server: validate HTTP server address: %w", errHTTPServerAddrInvalid)
|
|
|
|
}
|
|
|
|
|
2025-09-20 09:59:09 +05:00
|
|
|
h.httpMux = new(http.ServeMux)
|
|
|
|
|
|
|
|
// Default catch-all handler.
|
|
|
|
h.RegisterHandler("", "/", h.defaultHandler)
|
|
|
|
h.RegisterHandler(http.MethodGet, "/api/v1/socket", h.handleWebsocketRequest)
|
2025-09-15 09:33:25 +05:00
|
|
|
|
|
|
|
h.httpSrv = &http.Server{
|
|
|
|
Addr: httpSrvAddr,
|
2025-09-20 09:59:09 +05:00
|
|
|
Handler: h.httpMux,
|
2025-09-15 09:33:25 +05:00
|
|
|
ReadHeaderTimeout: time.Second * 3,
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpServer) handleWebsocketRequest(w http.ResponseWriter, r *http.Request) {
|
|
|
|
wsConn, err := websocket.Accept(w, r, &websocket.AcceptOptions{
|
|
|
|
OnPingReceived: func(_ context.Context, _ []byte) bool {
|
|
|
|
return true
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
h.logger.Error("Failed to accept WS connection!", "error", err.Error())
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err := wsConn.CloseNow(); err != nil {
|
|
|
|
h.logger.Warn("Failed to close WS connection in defer!", "error", err.Error())
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2025-09-20 09:59:09 +05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2025-09-15 09:33:25 +05:00
|
|
|
func (h *httpServer) startHTTPServer() {
|
|
|
|
h.logger.Info("Starting listening for HTTP requests.", "address", h.httpSrv.Addr)
|
|
|
|
|
|
|
|
if err := h.httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
|
|
h.logger.Warn("Error when listening to ", "error", err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpServer) stopHTTPServer() error {
|
|
|
|
h.logger.Info("Stopping HTTP server...")
|
|
|
|
|
|
|
|
if err := h.httpSrv.Shutdown(h.app.ContextWithTimeout(time.Second * 3)); err != nil {
|
|
|
|
return fmt.Errorf("stopping HTTP server: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|