From 00f4cab99291eec8ff206839a3e0ccd4f8dedbbf Mon Sep 17 00:00:00 2001 From: mountain Date: Wed, 20 Dec 2023 10:02:49 +0900 Subject: [PATCH] =?UTF-8?q?ws=20peer=20=EB=A5=BC=20=EC=A0=9C=EB=84=A4?= =?UTF-8?q?=EB=A6=AD=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wshandler/api_handler_peer.go | 187 ++++++++++++---------------------- wshandler/api_handler_test.go | 23 +++-- wshandler/wshandler_peer.go | 23 +++-- 3 files changed, 89 insertions(+), 144 deletions(-) diff --git a/wshandler/api_handler_peer.go b/wshandler/api_handler_peer.go index 64bc874..0d782ec 100644 --- a/wshandler/api_handler_peer.go +++ b/wshandler/api_handler_peer.go @@ -6,144 +6,108 @@ import ( "io" "reflect" "strings" - "unsafe" - "github.com/gorilla/websocket" "go.mongodb.org/mongo-driver/bson/primitive" "repositories.action2quare.com/ayo/gocommon/logger" ) -type peerApiFuncType func(any, io.Reader) (any, error) -type peerConnFuncType func(any, *websocket.Conn) -type peerDisconnFuncType func(any, string) +type PeerInterface interface { + ClientDisconnected(string) +} +type peerApiFuncType[T PeerInterface] func(T, io.Reader) (any, error) -type WebsocketPeerApiHandler struct { - methods map[string]peerApiFuncType - connfunc peerConnFuncType - disconnfunc peerDisconnFuncType +type WebsocketPeerApiHandler[T PeerInterface] struct { + methods map[string]peerApiFuncType[T] originalReceiverName string } -func MakeWebsocketPeerApiHandler[T any](receiverName string) WebsocketPeerApiHandler { - methods := make(map[string]peerApiFuncType) +func MakeWebsocketPeerApiHandler[T PeerInterface](receiverName string) WebsocketPeerApiHandler[T] { + methods := make(map[string]peerApiFuncType[T]) - var archetype *T + 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)) - } + if method.Name == ClientDisconnected { + continue + } - 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()) { + 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[1].Interface() == nil { - return out[0].Interface(), nil + if out[0].Interface() == nil { + return nil, nil } - return out[0].Interface(), out[1].Interface().(error) + return nil, out[0].Interface().(error) + } + } else { + outconv = func(out []reflect.Value) (any, error) { + return out[0].Interface(), nil } } - - methods[receiverName+"."+method.Name] = func(recv any, r io.Reader) (any, error) { - decoder := json.NewDecoder(r) - inargs := make([]any, len(intypes)) - - for i, intype := range intypes { - zerovalueptr := reflect.New(intype) - inargs[i] = zerovalueptr.Interface() + } 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 } - - 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 out[0].Interface(), out[1].Interface().(error) } } + + methods[receiverName+"."+method.Name] = func(recv T, r io.Reader) (any, error) { + decoder := json.NewDecoder(r) + 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{ + return WebsocketPeerApiHandler[T]{ 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 +type WebsocketPeerApiBroker[T PeerInterface] struct { + methods map[string]peerApiFuncType[T] + CreatePeer func(primitive.ObjectID) T } -func (hc *WebsocketPeerApiBroker) AddHandler(receiver WebsocketPeerApiHandler) { +func (hc *WebsocketPeerApiBroker[T]) AddHandler(receiver WebsocketPeerApiHandler[T]) { if hc.methods == nil { - hc.methods = make(map[string]peerApiFuncType) + hc.methods = make(map[string]peerApiFuncType[T]) } for k, v := range receiver.methods { @@ -151,32 +115,9 @@ func (hc *WebsocketPeerApiBroker) AddHandler(receiver WebsocketPeerApiHandler) { 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 *websocket.Conn) { - for _, v := range hc.connFuncs { - v(recv, c) - } -} - -func (hc *WebsocketPeerApiBroker) ClientDisconnected(recv any, reason string) { - for _, v := range hc.disconnFuncs { - v(recv, reason) - } -} - -func (hc *WebsocketPeerApiBroker) Call(recv any, funcname string, r io.Reader) (any, error) { +func (hc *WebsocketPeerApiBroker[T]) Call(recv T, funcname string, r io.Reader) (any, error) { if found := hc.methods[funcname]; found != nil { return found(recv, r) } diff --git a/wshandler/api_handler_test.go b/wshandler/api_handler_test.go index 33d4b77..3c35820 100644 --- a/wshandler/api_handler_test.go +++ b/wshandler/api_handler_test.go @@ -60,24 +60,27 @@ func TestUnmarshalToken(t *testing.T) { } -type peerHandler struct { +type testpeer struct { id string } -func (ph *peerHandler) ApiFunc1(arg1 string, arg2 int) error { +func (ph *testpeer) ApiFunc1(arg1 string, arg2 int) error { fmt.Println("ApiFunc1", ph.id, arg1, arg2) return errors.New("fake") } -func (ph *peerHandler) ApiFunc2(arg1 string, arg2 map[string]int) (string, error) { +func (ph *testpeer) ApiFunc2(arg1 string, arg2 map[string]int) (string, error) { fmt.Println("ApiFunc2", ph.id, arg1, arg2) return "success", nil } -func (ph *peerHandler) ApiFunc3(arg1 float64, arg2 []int) { +func (ph *testpeer) ApiFunc3(arg1 float64, arg2 []int) { fmt.Println("ApiFunc3", ph.id, arg1, arg2) } +func (ph *testpeer) ClientDisconnected(reason string) { +} + type dummySessionConsumer struct { } @@ -89,19 +92,19 @@ func (dsc *dummySessionConsumer) Touch(string) (session.Authorization, error) { } func TestPeerApiBroker(t *testing.T) { - handler := MakeWebsocketPeerApiHandler[peerHandler]("test") - ws := NewWebsocketPeerHandler(&dummySessionConsumer{}) + handler := MakeWebsocketPeerApiHandler[*testpeer]("test") + ws := NewWebsocketPeerHandler[*testpeer](&dummySessionConsumer{}) ws.AddHandler(handler) - peer := &peerHandler{ + tp := &testpeer{ id: "onlyone", } func1args, _ := json.Marshal([]any{string("arg1"), int(100)}) - ws.Call(peer, "test.ApiFunc1", bytes.NewBuffer(func1args)) + ws.Call(tp, "test.ApiFunc1", bytes.NewBuffer(func1args)) func1args, _ = json.Marshal([]any{string("arg1"), map[string]int{"arg2.key": 99}}) - ws.Call(peer, "test.ApiFunc2", bytes.NewBuffer(func1args)) + ws.Call(tp, "test.ApiFunc2", bytes.NewBuffer(func1args)) func1args, _ = json.Marshal([]any{float64(111.1), []int{99, 98}}) - ws.Call(peer, "test.ApiFunc3", bytes.NewBuffer(func1args)) + ws.Call(tp, "test.ApiFunc3", bytes.NewBuffer(func1args)) } diff --git a/wshandler/wshandler_peer.go b/wshandler/wshandler_peer.go index 763a0b7..3af127b 100644 --- a/wshandler/wshandler_peer.go +++ b/wshandler/wshandler_peer.go @@ -18,18 +18,18 @@ import ( "github.com/gorilla/websocket" ) -type WebsocketPeerHandler struct { - WebsocketPeerApiBroker +type WebsocketPeerHandler[T PeerInterface] struct { + WebsocketPeerApiBroker[T] sessionConsumer session.Consumer } -func NewWebsocketPeerHandler(consumer session.Consumer) WebsocketPeerHandler { - return WebsocketPeerHandler{ +func NewWebsocketPeerHandler[T PeerInterface](consumer session.Consumer) WebsocketPeerHandler[T] { + return WebsocketPeerHandler[T]{ sessionConsumer: consumer, } } -func (ws *WebsocketPeerHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string) error { +func (ws *WebsocketPeerHandler[T]) RegisterHandlers(serveMux *http.ServeMux, prefix string) error { url := gocommon.MakeHttpHandlerPattern(prefix, "ws") if *noAuthFlag { serveMux.HandleFunc(url, ws.upgrade_nosession) @@ -40,13 +40,15 @@ func (ws *WebsocketPeerHandler) RegisterHandlers(serveMux *http.ServeMux, prefix return nil } -func (ws *WebsocketPeerHandler) upgrade_core(conn *websocket.Conn, accid primitive.ObjectID, nonce uint32) { +func (ws *WebsocketPeerHandler[T]) upgrade_core(conn *websocket.Conn, accid primitive.ObjectID, nonce uint32) { go func(c *websocket.Conn, accid primitive.ObjectID) { peer := ws.CreatePeer(accid) - ws.ClientConnected(peer, c) - var closeReason string + defer func() { + peer.ClientDisconnected(closeReason) + }() + response := make([]byte, 255) for { response = response[:5] @@ -112,11 +114,10 @@ func (ws *WebsocketPeerHandler) upgrade_core(conn *websocket.Conn, accid primiti } } } - ws.ClientDisconnected(peer, closeReason) }(conn, accid) } -func (ws *WebsocketPeerHandler) upgrade_nosession(w http.ResponseWriter, r *http.Request) { +func (ws *WebsocketPeerHandler[T]) upgrade_nosession(w http.ResponseWriter, r *http.Request) { // 클라이언트 접속 defer func() { s := recover() @@ -166,7 +167,7 @@ func (ws *WebsocketPeerHandler) upgrade_nosession(w http.ResponseWriter, r *http ws.upgrade_core(conn, accid, nonce) } -func (ws *WebsocketPeerHandler) upgrade(w http.ResponseWriter, r *http.Request) { +func (ws *WebsocketPeerHandler[T]) upgrade(w http.ResponseWriter, r *http.Request) { // 클라이언트 접속 defer func() { s := recover()