package protocol import ( "context" "encoding/binary" "fmt" "io" "log" "net" "os" "sync" ) // Handler traite une requête et retourne une réponse. type Handler func(ctx context.Context, req *Request) *Response // Server écoute sur un Unix socket et dispatch les requêtes. type Server struct { socketPath string handler Handler listener net.Listener wg sync.WaitGroup ctx context.Context cancel context.CancelFunc } // NewServer crée un nouveau serveur. func NewServer(socketPath string, handler Handler) *Server { ctx, cancel := context.WithCancel(context.Background()) return &Server{ socketPath: socketPath, handler: handler, ctx: ctx, cancel: cancel, } } // Start démarre le serveur. func (s *Server) Start() error { // Supprimer le socket existant if err := os.RemoveAll(s.socketPath); err != nil { return fmt.Errorf("remove socket: %w", err) } listener, err := net.Listen("unix", s.socketPath) if err != nil { return fmt.Errorf("listen: %w", err) } s.listener = listener // Permissions socket if err := os.Chmod(s.socketPath, 0660); err != nil { return fmt.Errorf("chmod socket: %w", err) } log.Printf("[server] listening on %s", s.socketPath) go s.acceptLoop() return nil } // Stop arrête le serveur proprement. func (s *Server) Stop() { s.cancel() if s.listener != nil { s.listener.Close() } s.wg.Wait() os.RemoveAll(s.socketPath) log.Printf("[server] stopped") } // acceptLoop accepte les connexions entrantes. func (s *Server) acceptLoop() { for { conn, err := s.listener.Accept() if err != nil { select { case <-s.ctx.Done(): return default: log.Printf("[server] accept error: %v", err) continue } } s.wg.Add(1) go s.handleConn(conn) } } // handleConn gère une connexion client. func (s *Server) handleConn(conn net.Conn) { defer s.wg.Done() defer conn.Close() for { select { case <-s.ctx.Done(): return default: } // Lire la requête data, err := readMessage(conn) if err != nil { if err != io.EOF { log.Printf("[server] read error: %v", err) } return } // Décoder la requête req, err := DecodeRequest(data) if err != nil { resp := Failure("", "DECODE_ERROR", err.Error()) writeResponse(conn, resp) continue } // Traiter la requête resp := s.handler(s.ctx, req) // Envoyer la réponse if err := writeResponse(conn, resp); err != nil { log.Printf("[server] write error: %v", err) return } } } // readMessage lit un message length-prefixed. func readMessage(r io.Reader) ([]byte, error) { // Lire les 4 bytes de longueur lengthBuf := make([]byte, 4) if _, err := io.ReadFull(r, lengthBuf); err != nil { return nil, err } length := binary.BigEndian.Uint32(lengthBuf) if length == 0 || length > 10*1024*1024 { // Max 10MB return nil, fmt.Errorf("invalid message length: %d", length) } // Lire le payload data := make([]byte, length) if _, err := io.ReadFull(r, data); err != nil { return nil, err } return data, nil } // writeResponse écrit une réponse. func writeResponse(w io.Writer, resp *Response) error { data, err := Encode(resp) if err != nil { return err } return writeMessage(w, data) } // writeMessage écrit un message length-prefixed. func writeMessage(w io.Writer, data []byte) error { // Écrire la longueur lengthBuf := make([]byte, 4) binary.BigEndian.PutUint32(lengthBuf, uint32(len(data))) if _, err := w.Write(lengthBuf); err != nil { return err } // Écrire le payload _, err := w.Write(data) return err }