96 lines
2.2 KiB
Go
96 lines
2.2 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"sync/atomic"
|
|
|
|
"git.anygate.cloud/anygatecloud/strom/mc/proto"
|
|
"git.anygate.cloud/anygatecloud/strom/mc/proto_base"
|
|
"git.anygate.cloud/anygatecloud/strom/mc/text"
|
|
)
|
|
|
|
// Servee wraps a net.Conn in a new proto.Conn configured for the servee side.
|
|
func Servee(c net.Conn) (ret *proto.Conn) {
|
|
ret = proto.NewConn()
|
|
ret.Actor = proto_base.Servee
|
|
ret.Conn = c
|
|
ret.R = c
|
|
ret.W = c
|
|
|
|
ret.Log = ret.Log.With("conn", c.RemoteAddr())
|
|
return
|
|
}
|
|
|
|
type ConnAcceptor func(c net.Conn) (err error)
|
|
|
|
type Factory func(c *proto.Conn) (h ConnAcceptor, err error)
|
|
|
|
// ServeClient accepts a single client connection, creates a handler via the factory, and runs it.
|
|
func ServeClient(cNet net.Conn, factory Factory) {
|
|
c := Servee(cNet)
|
|
defer c.Close()
|
|
h, err := factory(c)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = h(c)
|
|
if errors.Is(err, StatusServedErr) {
|
|
return
|
|
}
|
|
if err != nil {
|
|
err = Kick(c, text.Pretty(err.Error()), "")
|
|
}
|
|
}
|
|
|
|
// StartServerWithFactory listens on the given address and spawns ServeClient for every accepted connection.
|
|
func StartServerWithFactory(listenAddr string, factory Factory) (err error) {
|
|
l, err := net.Listen("tcp", listenAddr)
|
|
if err != nil {
|
|
return
|
|
}
|
|
var cNet net.Conn
|
|
for {
|
|
cNet, err = l.Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
go ServeClient(cNet, factory)
|
|
}
|
|
}
|
|
|
|
var ShutDownServerErr = errors.New("server shutting down")
|
|
|
|
// StartServerWithOnConn listens on the given address and calls onConn for every accepted connection.
|
|
// The server loop stops when onConn returns ShutDownServerErr.
|
|
func StartServerWithOnConn(listenAddr string, onConn func(c *proto.Conn) (err error)) (err error) {
|
|
l, err := net.Listen("tcp", listenAddr)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer l.Close()
|
|
running := atomic.Bool{}
|
|
running.Store(true)
|
|
for running.Load() {
|
|
var cNet net.Conn
|
|
cNet, err = l.Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
go func() {
|
|
c := Servee(cNet)
|
|
defer c.Close()
|
|
connErr := onConn(c)
|
|
if errors.Is(connErr, ShutDownServerErr) {
|
|
running.Store(false)
|
|
}
|
|
c.Log.Error("closing connection", "err", connErr)
|
|
if connErr != nil {
|
|
connErr = Kick(c, text.Pretty(connErr.Error()), "")
|
|
}
|
|
}()
|
|
}
|
|
err = ShutDownServerErr
|
|
return
|
|
}
|