Files
gocommon/wshandler/api_handler.go

185 lines
4.4 KiB
Go

package wshandler
import (
"encoding/json"
"io"
"reflect"
"strings"
"unsafe"
"repositories.action2quare.com/ayo/gocommon/logger"
)
const (
ClientConnected = "ClientConnected"
ClientDisconnected = "ClientDisconnected"
)
type apiFuncType func(ApiCallContext)
type connFuncType func(*Conn, *Sender)
type disconnFuncType func(string, *Sender)
type WebsocketApiHandler struct {
methods map[string]apiFuncType
connfunc connFuncType
disconnfunc disconnFuncType
originalReceiverName string
}
type ApiCallContext struct {
CallBy *Sender
Arguments []any
}
func MakeWebsocketApiHandler[T any](receiver *T, receiverName string) WebsocketApiHandler {
methods := make(map[string]apiFuncType)
tp := reflect.TypeOf(receiver)
if len(receiverName) == 0 {
receiverName = tp.Elem().Name()
}
var connfunc connFuncType
var disconnfunc disconnFuncType
for i := 0; i < tp.NumMethod(); i++ {
method := tp.Method(i)
if method.Type.In(0) != tp {
continue
}
if method.Name == ClientConnected {
if method.Type.NumIn() != 3 {
continue
}
if method.Type.In(1) != reflect.TypeOf((*Conn)(nil)) {
continue
}
if method.Type.In(2) != reflect.TypeOf((*Sender)(nil)) {
continue
}
funcptr := method.Func.Pointer()
p1 := unsafe.Pointer(&funcptr)
p2 := unsafe.Pointer(&p1)
connfuncptr := (*func(*T, *Conn, *Sender))(p2)
connfunc = func(c *Conn, s *Sender) {
(*connfuncptr)(receiver, c, s)
}
} else if method.Name == ClientDisconnected {
if method.Type.NumIn() != 3 {
continue
}
if method.Type.In(1) != reflect.TypeOf("") {
continue
}
if method.Type.In(2) != reflect.TypeOf((*Sender)(nil)) {
continue
}
funcptr := method.Func.Pointer()
p1 := unsafe.Pointer(&funcptr)
p2 := unsafe.Pointer(&p1)
disconnfuncptr := (*func(*T, string, *Sender))(p2)
disconnfunc = func(msg string, s *Sender) {
(*disconnfuncptr)(receiver, msg, s)
}
} else {
if method.Type.NumIn() != 2 {
continue
}
if method.Type.In(1) != reflect.TypeOf((*ApiCallContext)(nil)).Elem() {
continue
}
funcptr := method.Func.Pointer()
p1 := unsafe.Pointer(&funcptr)
p2 := unsafe.Pointer(&p1)
apifuncptr := (*func(*T, ApiCallContext))(p2)
methods[receiverName+"."+method.Name] = func(ctx ApiCallContext) {
(*apifuncptr)(receiver, ctx)
}
}
}
return WebsocketApiHandler{
methods: methods,
connfunc: connfunc,
disconnfunc: disconnfunc,
originalReceiverName: tp.Elem().Name(),
}
}
type WebsocketApiBroker struct {
methods map[string]apiFuncType
methods_dup map[string][]apiFuncType
connFuncs []connFuncType
disconnFuncs []disconnFuncType
}
func (hc *WebsocketApiBroker) AddHandler(receiver WebsocketApiHandler) {
if hc.methods == nil {
hc.methods = make(map[string]apiFuncType)
hc.methods_dup = make(map[string][]apiFuncType)
}
for k, v := range receiver.methods {
ab := strings.Split(k, ".")
logger.Printf("ws api registered : %s.%s -> %s\n", receiver.originalReceiverName, ab[1], k)
hc.methods_dup[k] = append(hc.methods_dup[k], v)
if len(hc.methods_dup[k]) > 1 {
chain := hc.methods_dup[k]
hc.methods[k] = func(ctx ApiCallContext) {
for _, f := range chain {
f(ctx)
}
}
} else {
hc.methods[k] = v
}
}
if receiver.connfunc != nil {
logger.Printf("ws api registered : %s.ClientConnected\n", receiver.originalReceiverName)
hc.connFuncs = append(hc.connFuncs, receiver.connfunc)
}
if receiver.disconnfunc != nil {
// disconnfunc은 역순
logger.Printf("ws api registered : %s.ClientDisconnected\n", receiver.originalReceiverName)
hc.disconnFuncs = append([]disconnFuncType{receiver.disconnfunc}, hc.disconnFuncs...)
}
}
func (hc *WebsocketApiBroker) ClientConnected(c *wsconn) {
for _, v := range hc.connFuncs {
v(c.Conn, c.sender)
}
}
func (hc *WebsocketApiBroker) ClientDisconnected(c *wsconn) {
for _, v := range hc.disconnFuncs {
v(c.closeMessage, c.sender)
}
}
func (hc *WebsocketApiBroker) Call(callby *Sender, funcname string, r io.Reader) {
if found := hc.methods[funcname]; found != nil {
var args []any
if r != nil {
dec := json.NewDecoder(r)
if err := dec.Decode(&args); err != nil {
logger.Println("WebsocketApiBroker.Call failed. decode returns err :", err)
}
}
found(ApiCallContext{
CallBy: callby,
Arguments: args,
})
} else {
logger.Println("api is not found :", funcname)
}
}