From 8e4f8c2fcc77515f20a8cb1cd03b305a4695629c Mon Sep 17 00:00:00 2001 From: Tordarus Date: Thu, 31 Jul 2025 18:34:16 +0200 Subject: [PATCH] initial commit --- go.mod | 12 +++++++ go.sum | 6 ++++ multiwriter.go | 28 ++++++++++++++++ server.go | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 multiwriter.go create mode 100644 server.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9cafa32 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module git.tordarus.net/tordarus/sock + +go 1.24.1 + +toolchain go1.24.5 + +require ( + git.tordarus.net/tordarus/cmap v0.0.5 + git.tordarus.net/tordarus/slices v0.0.15 +) + +require git.tordarus.net/tordarus/gmath v0.0.7 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e51f87b --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +git.tordarus.net/tordarus/cmap v0.0.5 h1:g2STu3iMXCLn96JZ/f0iN5hbzct9cI5B8+V8ATB4ryE= +git.tordarus.net/tordarus/cmap v0.0.5/go.mod h1:Wx2snnIzdDrmONR9X5bt5aFq8E8EDCK5Zy50DmRwK1M= +git.tordarus.net/tordarus/gmath v0.0.7 h1:tR48idt9AUL0r556ww3ZxByTKJEr6NWCTlhl2ihzYxQ= +git.tordarus.net/tordarus/gmath v0.0.7/go.mod h1:mO7aPlvNrGVE9UFXEuuACjZgMDsM63l3OcQy6xSQnoE= +git.tordarus.net/tordarus/slices v0.0.15 h1:qxKS+7BCZ/LzQbRCvdDFoLQXaJZ2C0GfVju/kPt1r3g= +git.tordarus.net/tordarus/slices v0.0.15/go.mod h1:eJBw6pSDNivPI0l4e0sGKUJzou/lbzHflXdAUzp1g4o= diff --git a/multiwriter.go b/multiwriter.go new file mode 100644 index 0000000..98fc8e2 --- /dev/null +++ b/multiwriter.go @@ -0,0 +1,28 @@ +package sock + +import "io" + +type multiWriter struct { + writers []io.Writer +} + +var _ io.Writer = &multiWriter{} + +func (t *multiWriter) Write(p []byte) (n int, err error) { + for _, w := range t.writers { + w.Write(p) + } + return len(p), nil +} + +func newMultiWriter(writers ...io.Writer) io.Writer { + allWriters := make([]io.Writer, 0, len(writers)) + for _, w := range writers { + if mw, ok := w.(*multiWriter); ok { + allWriters = append(allWriters, mw.writers...) + } else { + allWriters = append(allWriters, w) + } + } + return &multiWriter{allWriters} +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..6f1a92f --- /dev/null +++ b/server.go @@ -0,0 +1,89 @@ +package sock + +import ( + "io" + "net" + "os" + "path/filepath" + + "git.tordarus.net/tordarus/cmap" + "git.tordarus.net/tordarus/slices" +) + +type Server struct { + socketPath string + server net.Listener + clients *cmap.Map[net.Conn, struct{}] + + onNewClient func(client net.Conn) +} + +func Listen(socketPath string, onNewClient func(client net.Conn)) (*Server, error) { + absPath, err := filepath.Abs(socketPath) + if err != nil { + return nil, err + } + + os.Remove(absPath) + server, err := net.Listen("unix", absPath) + if err != nil { + return nil, err + } + + s := &Server{ + socketPath: absPath, + server: server, + clients: cmap.New[net.Conn, struct{}](), + onNewClient: onNewClient, + } + + go s.handleClients() + + return s, nil +} + +func (s *Server) Broadcast(r io.Reader) error { + clients := slices.Map(s.clients.Keys(), func(c net.Conn) io.Writer { return c }) + w := newMultiWriter(clients...) + + if _, err := io.Copy(w, r); err != nil { + return err + } + + return nil +} + +func (s *Server) handleClients() { + for client, err := s.server.Accept(); err == nil; client, err = s.server.Accept() { + go s.handleClient(client) + } +} + +func (s *Server) handleClient(client net.Conn) { + s.clients.Put(client, struct{}{}) + defer s.clients.Delete(client) + + if s.onNewClient != nil { + s.onNewClient(client) + } + + data := make([]byte, 1024) + for _, err := client.Read(data); err == nil; _, err = client.Read(data) { + } +} + +func (s *Server) Close() error { + for client := range s.clients.Iterate() { + client.Close() + } + + if err := s.server.Close(); err != nil { + return err + } + + if err := os.Remove(s.socketPath); err != nil { + return err + } + + return nil +}