2023-09-08 11:35:57 +09:00
|
|
|
package wshandler
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"io"
|
|
|
|
|
"reflect"
|
2023-09-19 18:50:17 +09:00
|
|
|
"strings"
|
2023-09-08 11:35:57 +09:00
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
|
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
ClientConnected = "ClientConnected"
|
|
|
|
|
ClientDisconnected = "ClientDisconnected"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type apiFuncType func(ApiCallContext)
|
2024-07-24 16:18:40 +09:00
|
|
|
type connFuncType func(*Conn, *Sender)
|
2023-11-10 16:40:42 +09:00
|
|
|
type disconnFuncType func(string, *Sender)
|
2023-09-08 11:35:57 +09:00
|
|
|
|
|
|
|
|
type WebsocketApiHandler struct {
|
2023-09-19 18:50:17 +09:00
|
|
|
methods map[string]apiFuncType
|
|
|
|
|
connfunc connFuncType
|
2023-11-10 16:40:42 +09:00
|
|
|
disconnfunc disconnFuncType
|
2023-09-19 18:50:17 +09:00
|
|
|
originalReceiverName string
|
2023-09-08 11:35:57 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-19 18:50:17 +09:00
|
|
|
var connfunc connFuncType
|
2023-11-10 16:40:42 +09:00
|
|
|
var disconnfunc disconnFuncType
|
2023-09-08 11:35:57 +09:00
|
|
|
|
|
|
|
|
for i := 0; i < tp.NumMethod(); i++ {
|
|
|
|
|
method := tp.Method(i)
|
|
|
|
|
if method.Type.In(0) != tp {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if method.Name == ClientConnected {
|
2023-09-19 18:50:17 +09:00
|
|
|
if method.Type.NumIn() != 3 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-07-24 16:18:40 +09:00
|
|
|
if method.Type.In(1) != reflect.TypeOf((*Conn)(nil)) {
|
2023-09-19 18:50:17 +09:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if method.Type.In(2) != reflect.TypeOf((*Sender)(nil)) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
funcptr := method.Func.Pointer()
|
|
|
|
|
p1 := unsafe.Pointer(&funcptr)
|
|
|
|
|
p2 := unsafe.Pointer(&p1)
|
2024-07-24 16:18:40 +09:00
|
|
|
connfuncptr := (*func(*T, *Conn, *Sender))(p2)
|
2023-09-19 18:50:17 +09:00
|
|
|
|
2024-07-24 16:18:40 +09:00
|
|
|
connfunc = func(c *Conn, s *Sender) {
|
2023-09-19 18:50:17 +09:00
|
|
|
(*connfuncptr)(receiver, c, s)
|
2023-09-08 11:35:57 +09:00
|
|
|
}
|
|
|
|
|
} else if method.Name == ClientDisconnected {
|
2023-09-19 18:50:17 +09:00
|
|
|
if method.Type.NumIn() != 3 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2023-11-10 16:40:42 +09:00
|
|
|
if method.Type.In(1) != reflect.TypeOf("") {
|
2023-09-19 18:50:17 +09:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if method.Type.In(2) != reflect.TypeOf((*Sender)(nil)) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
funcptr := method.Func.Pointer()
|
|
|
|
|
p1 := unsafe.Pointer(&funcptr)
|
|
|
|
|
p2 := unsafe.Pointer(&p1)
|
2023-11-10 16:40:42 +09:00
|
|
|
disconnfuncptr := (*func(*T, string, *Sender))(p2)
|
2023-09-19 18:50:17 +09:00
|
|
|
|
2023-11-10 16:40:42 +09:00
|
|
|
disconnfunc = func(msg string, s *Sender) {
|
|
|
|
|
(*disconnfuncptr)(receiver, msg, s)
|
2023-09-08 11:35:57 +09:00
|
|
|
}
|
|
|
|
|
} else {
|
2023-09-19 18:50:17 +09:00
|
|
|
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)
|
2023-09-08 11:35:57 +09:00
|
|
|
methods[receiverName+"."+method.Name] = func(ctx ApiCallContext) {
|
2023-09-19 18:50:17 +09:00
|
|
|
(*apifuncptr)(receiver, ctx)
|
2023-09-08 11:35:57 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return WebsocketApiHandler{
|
2023-09-19 18:50:17 +09:00
|
|
|
methods: methods,
|
|
|
|
|
connfunc: connfunc,
|
|
|
|
|
disconnfunc: disconnfunc,
|
|
|
|
|
originalReceiverName: tp.Elem().Name(),
|
2023-09-08 11:35:57 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type WebsocketApiBroker struct {
|
|
|
|
|
methods map[string]apiFuncType
|
2023-09-19 18:50:17 +09:00
|
|
|
methods_dup map[string][]apiFuncType
|
|
|
|
|
connFuncs []connFuncType
|
2023-11-10 16:40:42 +09:00
|
|
|
disconnFuncs []disconnFuncType
|
2023-09-08 11:35:57 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (hc *WebsocketApiBroker) AddHandler(receiver WebsocketApiHandler) {
|
|
|
|
|
if hc.methods == nil {
|
|
|
|
|
hc.methods = make(map[string]apiFuncType)
|
2023-09-19 18:50:17 +09:00
|
|
|
hc.methods_dup = make(map[string][]apiFuncType)
|
2023-09-08 11:35:57 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for k, v := range receiver.methods {
|
2023-09-19 18:50:17 +09:00
|
|
|
ab := strings.Split(k, ".")
|
2023-09-21 13:44:39 +09:00
|
|
|
logger.Printf("ws api registered : %s.%s -> %s\n", receiver.originalReceiverName, ab[1], k)
|
2023-09-19 18:50:17 +09:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
2023-09-08 11:35:57 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if receiver.connfunc != nil {
|
2023-09-21 13:44:39 +09:00
|
|
|
logger.Printf("ws api registered : %s.ClientConnected\n", receiver.originalReceiverName)
|
2023-09-08 11:35:57 +09:00
|
|
|
hc.connFuncs = append(hc.connFuncs, receiver.connfunc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if receiver.disconnfunc != nil {
|
|
|
|
|
// disconnfunc은 역순
|
2023-09-21 13:44:39 +09:00
|
|
|
logger.Printf("ws api registered : %s.ClientDisconnected\n", receiver.originalReceiverName)
|
2023-11-10 16:40:42 +09:00
|
|
|
hc.disconnFuncs = append([]disconnFuncType{receiver.disconnfunc}, hc.disconnFuncs...)
|
2023-09-19 18:50:17 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2023-11-10 16:40:42 +09:00
|
|
|
v(c.closeMessage, c.sender)
|
2023-09-08 11:35:57 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (hc *WebsocketApiBroker) Call(callby *Sender, funcname string, r io.Reader) {
|
2023-09-19 18:50:17 +09:00
|
|
|
if found := hc.methods[funcname]; found != nil {
|
2023-09-08 11:35:57 +09:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|