summaryrefslogtreecommitdiff
path: root/internal/server.go
diff options
context:
space:
mode:
authortjpcc <tjp@ctrl-c.club>2023-01-28 14:52:35 -0700
committertjpcc <tjp@ctrl-c.club>2023-01-28 15:01:41 -0700
commit66a1b1f39a1e1d5499b548b36d18c8daa872d7da (patch)
tree96471dbd5486ede1a908790ac23e0c55b226dfad /internal/server.go
parenta27b879accb191b6a6c6e76a6251ed751967f73a (diff)
gopher support.
Some of the contrib packages were originally built gemini-specific and had to be refactored into generic core functionality and thin protocol-specific wrappers for each of gemini and gopher.
Diffstat (limited to 'internal/server.go')
-rw-r--r--internal/server.go126
1 files changed, 126 insertions, 0 deletions
diff --git a/internal/server.go b/internal/server.go
new file mode 100644
index 0000000..38e478c
--- /dev/null
+++ b/internal/server.go
@@ -0,0 +1,126 @@
+package internal
+
+import (
+ "context"
+ "net"
+ "sync"
+
+ "tildegit.org/tjp/gus/logging"
+)
+
+type Server struct {
+ Ctx context.Context
+ Cancel context.CancelFunc
+ Wg *sync.WaitGroup
+ Listener net.Listener
+ HandleConn connHandler
+ ErrorLog logging.Logger
+ Host string
+ NetworkAddr net.Addr
+}
+
+type connHandler func(net.Conn)
+
+func NewServer(
+ ctx context.Context,
+ hostname string,
+ network string,
+ address string,
+ errorLog logging.Logger,
+ handleConn connHandler,
+) (Server, error) {
+ listener, err := net.Listen(network, address)
+ if err != nil {
+ return Server{}, err
+ }
+
+ networkAddr := listener.Addr()
+ ctx, cancel := context.WithCancel(ctx)
+
+ return Server{
+ Ctx: ctx,
+ Cancel: cancel,
+ Wg: &sync.WaitGroup{},
+ Listener: listener,
+ HandleConn: handleConn,
+ ErrorLog: errorLog,
+ Host: hostname,
+ NetworkAddr: networkAddr,
+ }, nil
+}
+
+func (s *Server) Serve() error {
+ s.Wg.Add(1)
+ defer s.Wg.Done()
+
+ s.propagateClose()
+
+ for {
+ conn, err := s.Listener.Accept()
+ if err != nil {
+ if s.Closed() {
+ err = nil
+ } else {
+ _ = s.ErrorLog.Log("msg", "accept error", "error", err)
+ }
+
+ return err
+ }
+
+ s.Wg.Add(1)
+ go func() {
+ defer s.Wg.Done()
+ defer func() {
+ _ = conn.Close()
+ }()
+
+ s.HandleConn(conn)
+ }()
+ }
+}
+
+func (s *Server) Hostname() string {
+ host, _, _ := net.SplitHostPort(s.Host)
+ return host
+}
+
+func (s *Server) Port() string {
+ _, port, _ := net.SplitHostPort(s.Host)
+ return port
+}
+
+func (s *Server) Network() string {
+ return s.NetworkAddr.Network()
+}
+
+func (s *Server) Address() string {
+ return s.NetworkAddr.String()
+}
+
+func (s *Server) Close() {
+ s.Cancel()
+ s.Wg.Wait()
+}
+
+func (s *Server) LogError(keyvals ...any) error {
+ return s.ErrorLog.Log(keyvals...)
+}
+
+func (s *Server) Closed() bool {
+ select {
+ case <-s.Ctx.Done():
+ return true
+ default:
+ return false
+ }
+}
+
+func (s *Server) propagateClose() {
+ s.Wg.Add(1)
+ go func() {
+ defer s.Wg.Done()
+
+ <-s.Ctx.Done()
+ _ = s.Listener.Close()
+ }()
+}