http, websocket api handler 추가

This commit is contained in:
2023-09-19 18:50:17 +09:00
parent 1af5d72819
commit 49a3722a7e
5 changed files with 221 additions and 64 deletions

View File

@ -4,8 +4,10 @@ import (
"encoding/json"
"io"
"reflect"
"strings"
"unsafe"
"github.com/gorilla/websocket"
"repositories.action2quare.com/ayo/gocommon/logger"
)
@ -15,11 +17,13 @@ const (
)
type apiFuncType func(ApiCallContext)
type connFuncType func(*websocket.Conn, *Sender)
type WebsocketApiHandler struct {
methods map[string]apiFuncType
connfunc apiFuncType
disconnfunc apiFuncType
methods map[string]apiFuncType
connfunc connFuncType
disconnfunc connFuncType
originalReceiverName string
}
type ApiCallContext struct {
@ -35,92 +39,133 @@ func MakeWebsocketApiHandler[T any](receiver *T, receiverName string) WebsocketA
receiverName = tp.Elem().Name()
}
var connfunc apiFuncType
var disconnfunc apiFuncType
var connfunc connFuncType
var disconnfunc connFuncType
for i := 0; i < tp.NumMethod(); i++ {
method := tp.Method(i)
if method.Type.NumIn() != 2 {
continue
}
if method.Type.In(0) != tp {
continue
}
if method.Type.In(1) != reflect.TypeOf((*ApiCallContext)(nil)).Elem() {
continue
}
funcptr := method.Func.Pointer()
p1 := unsafe.Pointer(&funcptr)
p2 := unsafe.Pointer(&p1)
testfunc := (*func(*T, ApiCallContext))(p2)
if method.Name == ClientConnected {
connfunc = func(ctx ApiCallContext) {
(*testfunc)(receiver, ctx)
if method.Type.NumIn() != 3 {
continue
}
if method.Type.In(1) != reflect.TypeOf((*websocket.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, *websocket.Conn, *Sender))(p2)
connfunc = func(c *websocket.Conn, s *Sender) {
(*connfuncptr)(receiver, c, s)
}
} else if method.Name == ClientDisconnected {
disconnfunc = func(ctx ApiCallContext) {
(*testfunc)(receiver, ctx)
if method.Type.NumIn() != 3 {
continue
}
if method.Type.In(1) != reflect.TypeOf((*websocket.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)
disconnfuncptr := (*func(*T, *websocket.Conn, *Sender))(p2)
disconnfunc = func(c *websocket.Conn, s *Sender) {
(*disconnfuncptr)(receiver, c, 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) {
(*testfunc)(receiver, ctx)
(*apifuncptr)(receiver, ctx)
}
}
}
return WebsocketApiHandler{
methods: methods,
connfunc: connfunc,
disconnfunc: disconnfunc,
methods: methods,
connfunc: connfunc,
disconnfunc: disconnfunc,
originalReceiverName: tp.Elem().Name(),
}
}
type WebsocketApiBroker struct {
methods map[string]apiFuncType
connFuncs []apiFuncType
disconnFuncs []apiFuncType
methods_dup map[string][]apiFuncType
connFuncs []connFuncType
disconnFuncs []connFuncType
}
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 {
logger.Println("http api registered :", k)
hc.methods[k] = v
ab := strings.Split(k, ".")
logger.Printf("websocket 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("websocket api registered : %s.ClientConnected\n", receiver.originalReceiverName)
hc.connFuncs = append(hc.connFuncs, receiver.connfunc)
}
if receiver.disconnfunc != nil {
// disconnfunc은 역순
hc.disconnFuncs = append([]apiFuncType{receiver.disconnfunc}, hc.disconnFuncs...)
logger.Printf("websocket api registered : %s.ClientDisconnected\n", receiver.originalReceiverName)
hc.disconnFuncs = append([]connFuncType{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.Conn, c.sender)
}
}
func (hc *WebsocketApiBroker) Call(callby *Sender, funcname string, r io.Reader) {
if funcname == ClientConnected {
for _, v := range hc.connFuncs {
v(ApiCallContext{
CallBy: callby,
Arguments: nil,
})
}
} else if funcname == ClientDisconnected {
for _, v := range hc.disconnFuncs {
v(ApiCallContext{
CallBy: callby,
Arguments: nil,
})
}
} else if found := hc.methods[funcname]; found != nil {
if found := hc.methods[funcname]; found != nil {
var args []any
if r != nil {
dec := json.NewDecoder(r)

View File

@ -85,6 +85,8 @@ type send_msg_queue_elem struct {
}
type WebsocketHandler struct {
WebsocketApiBroker
redisMsgChanName string
redisCmdChanName string
redisSync *redis.Client
@ -93,7 +95,6 @@ type WebsocketHandler struct {
localDeliveryChan chan any
sendMsgChan chan send_msg_queue_elem
wsApiBroker WebsocketApiBroker
connWaitGroup sync.WaitGroup
sessionConsumer session.Consumer
}
@ -121,7 +122,7 @@ func NewWebsocketHandler(consumer session.Consumer, redisUrl string) (*Websocket
redisSync, err := gocommon.NewRedisClient(redisUrl)
if err != nil {
return nil, err
return nil, logger.ErrorWithCallStack(err)
}
sendchan := make(chan send_msg_queue_elem, 1000)
@ -153,10 +154,6 @@ func NewWebsocketHandler(consumer session.Consumer, redisUrl string) (*Websocket
}, nil
}
func (ws *WebsocketHandler) RegisterApiHandler(handler WebsocketApiHandler) {
ws.wsApiBroker.AddHandler(handler)
}
func (ws *WebsocketHandler) Start(ctx context.Context) {
ws.connWaitGroup.Add(1)
go ws.mainLoop(ctx)
@ -260,7 +257,7 @@ func (ws *WebsocketHandler) mainLoop(ctx context.Context) {
defer func() {
for _, conn := range entireConns {
ws.wsApiBroker.Call(conn.sender, ClientDisconnected, nil)
ws.Call(conn.sender, ClientDisconnected, nil)
conn.Close()
}
}()
@ -414,10 +411,12 @@ func (ws *WebsocketHandler) mainLoop(ctx context.Context) {
case c := <-ws.connInOutChan:
if c.Conn == nil {
delete(entireConns, c.sender.Accid.Hex())
go ws.wsApiBroker.Call(c.sender, ClientDisconnected, nil)
logger.Println("ClientDisconnected :", c.sender.Alias)
go ws.ClientDisconnected(c)
} else {
entireConns[c.sender.Accid.Hex()] = c
go ws.wsApiBroker.Call(c.sender, ClientConnected, nil)
logger.Println("ClientConnected :", c.sender.Alias)
go ws.ClientConnected(c)
}
}
}
@ -451,7 +450,7 @@ func upgrade_core(ws *WebsocketHandler, conn *websocket.Conn, accid primitive.Ob
r.Read(size[:])
cmd := make([]byte, size[0])
r.Read(cmd)
ws.wsApiBroker.Call(newconn.sender, string(cmd), r)
ws.Call(newconn.sender, string(cmd), r)
}
}
ws.connWaitGroup.Done()