package wshandler import ( "bytes" "encoding/json" "reflect" "strings" "unsafe" "github.com/gorilla/websocket" "go.mongodb.org/mongo-driver/bson/primitive" "repositories.action2quare.com/ayo/gocommon/logger" ) type peerApiFuncType func(any, []byte) (any, error) type peerConnFuncType func(any, *websocket.Conn) type peerDisconnFuncType func(any, string) type WebsocketPeerApiHandler struct { methods map[string]peerApiFuncType connfunc peerConnFuncType disconnfunc peerDisconnFuncType originalReceiverName string } func MakeWebsocketPeerApiHandler[T any](receiverName string) WebsocketPeerApiHandler { methods := make(map[string]peerApiFuncType) var archetype *T tp := reflect.TypeOf(archetype) if len(receiverName) == 0 { receiverName = tp.Elem().Name() } var connfunc peerConnFuncType var disconnfunc peerDisconnFuncType 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() != 2 { continue } if method.Type.In(1) != reflect.TypeOf((*websocket.Conn)(nil)) { continue } funcptr := method.Func.Pointer() p1 := unsafe.Pointer(&funcptr) p2 := unsafe.Pointer(&p1) connfuncptr := (*func(*T, *websocket.Conn))(p2) connfunc = func(r any, c *websocket.Conn) { (*connfuncptr)(r.(*T), c) } } else if method.Name == ClientDisconnected { if method.Type.NumIn() != 2 { continue } if method.Type.In(1) != reflect.TypeOf("") { continue } funcptr := method.Func.Pointer() p1 := unsafe.Pointer(&funcptr) p2 := unsafe.Pointer(&p1) disconnfuncptr := (*func(*T, string))(p2) disconnfunc = func(r any, msg string) { (*disconnfuncptr)(r.(*T), msg) } } else { var intypes []reflect.Type for i := 1; i < method.Type.NumIn(); i++ { intypes = append(intypes, method.Type.In(i)) } var outconv func([]reflect.Value) (any, error) if method.Type.NumOut() == 0 { outconv = func([]reflect.Value) (any, error) { return nil, nil } } else if method.Type.NumOut() == 1 { if method.Type.Out(0).Implements(reflect.TypeOf((*error)(nil)).Elem()) { outconv = func(out []reflect.Value) (any, error) { if out[0].Interface() == nil { return nil, nil } return nil, out[0].Interface().(error) } } else { outconv = func(out []reflect.Value) (any, error) { return out[0].Interface(), nil } } } else if method.Type.NumOut() == 2 && method.Type.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { outconv = func(out []reflect.Value) (any, error) { if out[1].Interface() == nil { return out[0].Interface(), nil } return out[0].Interface(), out[1].Interface().(error) } } methods[receiverName+"."+method.Name] = func(recv any, buff []byte) (any, error) { decoder := json.NewDecoder(bytes.NewBuffer(buff)) inargs := make([]any, len(intypes)) for i, intype := range intypes { zerovalueptr := reflect.New(intype) inargs[i] = zerovalueptr.Interface() } err := decoder.Decode(&inargs) if err != nil { return nil, err } reflectargs := make([]reflect.Value, 0, len(inargs)+1) reflectargs = append(reflectargs, reflect.ValueOf(recv)) for _, p := range inargs { reflectargs = append(reflectargs, reflect.ValueOf(p).Elem()) } return outconv(method.Func.Call(reflectargs)) } } } return WebsocketPeerApiHandler{ methods: methods, connfunc: connfunc, disconnfunc: disconnfunc, originalReceiverName: tp.Elem().Name(), } } type WebsocketPeerApiBroker struct { methods map[string]peerApiFuncType connFuncs []peerConnFuncType disconnFuncs []peerDisconnFuncType CreatePeer func(primitive.ObjectID) any } func (hc *WebsocketPeerApiBroker) AddHandler(receiver WebsocketPeerApiHandler) { if hc.methods == nil { hc.methods = make(map[string]peerApiFuncType) } 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[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([]peerDisconnFuncType{receiver.disconnfunc}, hc.disconnFuncs...) } } func (hc *WebsocketPeerApiBroker) ClientConnected(recv any, c *wsconn) { for _, v := range hc.connFuncs { v(recv, c.Conn) } } func (hc *WebsocketPeerApiBroker) ClientDisconnected(recv any, c *wsconn) { for _, v := range hc.disconnFuncs { v(recv, c.closeMessage) } } func (hc *WebsocketPeerApiBroker) Call(recv any, funcname string, buff []byte) { if found := hc.methods[funcname]; found != nil { _, err := found(recv, buff) if err != nil { logger.Println("api call is failed. err :", err) } } else { logger.Println("api is not found :", funcname) } }