2025-08-20 00:24:59 +04:00
|
|
|
package httpserver
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"io"
|
|
|
|
|
"net"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
|
|
|
|
"go-proxy-cache/internal/cache/service"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Server represents the HTTP cache server
|
|
|
|
|
type Server struct {
|
|
|
|
|
cacheService *service.CacheService
|
|
|
|
|
logger *zap.Logger
|
|
|
|
|
server *http.Server
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewServer creates a new cache HTTP server
|
|
|
|
|
func NewServer(cacheService *service.CacheService, logger *zap.Logger) *Server {
|
|
|
|
|
return &Server{
|
|
|
|
|
cacheService: cacheService,
|
|
|
|
|
logger: logger,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StartUnixSocket starts the HTTP server on a Unix socket
|
|
|
|
|
func (s *Server) StartUnixSocket(socketPath string) error {
|
|
|
|
|
// Remove existing socket file
|
|
|
|
|
if err := os.RemoveAll(socketPath); err != nil {
|
|
|
|
|
s.logger.Warn("Failed to remove existing socket file", zap.String("path", socketPath), zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create Unix socket listener
|
|
|
|
|
listener, err := net.Listen("unix", socketPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-20 21:36:44 +04:00
|
|
|
// Set socket permissions (readable/writable by all users for cross-container access)
|
|
|
|
|
if err := os.Chmod(socketPath, 0666); err != nil {
|
2025-08-20 00:24:59 +04:00
|
|
|
s.logger.Warn("Failed to set socket permissions", zap.String("path", socketPath), zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
router := s.createRouter()
|
|
|
|
|
|
|
|
|
|
s.server = &http.Server{
|
|
|
|
|
Handler: router,
|
|
|
|
|
ReadTimeout: 30 * time.Second,
|
|
|
|
|
WriteTimeout: 30 * time.Second,
|
|
|
|
|
IdleTimeout: 60 * time.Second,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.logger.Info("Starting cache HTTP server on Unix socket", zap.String("socket_path", socketPath))
|
|
|
|
|
return s.server.Serve(listener)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stop stops the HTTP server
|
|
|
|
|
func (s *Server) Stop(ctx context.Context) error {
|
|
|
|
|
s.logger.Info("Stopping cache HTTP server")
|
2025-09-17 17:26:23 +04:00
|
|
|
|
|
|
|
|
// Stop main server
|
|
|
|
|
if s.server != nil {
|
|
|
|
|
return s.server.Shutdown(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
2025-08-20 00:24:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// createRouter creates and configures the HTTP router
|
|
|
|
|
func (s *Server) createRouter() *mux.Router {
|
|
|
|
|
router := mux.NewRouter()
|
|
|
|
|
|
|
|
|
|
// Cache endpoints
|
|
|
|
|
router.HandleFunc("/cache/get", s.handleGet).Methods("POST")
|
|
|
|
|
router.HandleFunc("/cache/set", s.handleSet).Methods("POST")
|
|
|
|
|
|
|
|
|
|
// Health check
|
|
|
|
|
router.HandleFunc("/health", s.handleHealth).Methods("GET")
|
|
|
|
|
|
|
|
|
|
// Cache info endpoint (equivalent to cache rules check)
|
|
|
|
|
router.HandleFunc("/cache/info", s.handleCacheInfo).Methods("POST")
|
|
|
|
|
|
|
|
|
|
return router
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handleHealth handles health check requests
|
|
|
|
|
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
s.writeResponse(w, map[string]interface{}{
|
|
|
|
|
"status": "healthy",
|
|
|
|
|
"time": time.Now().UTC(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseRequest parses JSON request body
|
|
|
|
|
func (s *Server) parseRequest(r *http.Request, v interface{}) error {
|
|
|
|
|
body, err := io.ReadAll(r.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2025-12-30 19:44:53 +04:00
|
|
|
defer func() { _ = r.Body.Close() }()
|
2025-08-20 00:24:59 +04:00
|
|
|
|
|
|
|
|
return json.Unmarshal(body, v)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// writeResponse writes JSON response
|
|
|
|
|
func (s *Server) writeResponse(w http.ResponseWriter, v interface{}) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
if err := json.NewEncoder(w).Encode(v); err != nil {
|
|
|
|
|
s.logger.Error("Failed to write response", zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// writeErrorResponse writes error response
|
|
|
|
|
func (s *Server) writeErrorResponse(w http.ResponseWriter, message string, statusCode int) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
w.WriteHeader(statusCode)
|
|
|
|
|
response := map[string]interface{}{
|
|
|
|
|
"success": false,
|
|
|
|
|
"error": message,
|
|
|
|
|
}
|
|
|
|
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
|
|
|
s.logger.Error("Failed to write error response", zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
}
|