Browse Source

ws req -> res

master
tangmingyou 4 years ago
parent
commit
943b9286a3
  1. 21
      api/operation.go
  2. 2
      api/poker.pb.go
  3. 9
      etc/config.toml
  4. 14
      internal/server/server.go
  5. 42
      internal/server/ws.go
  6. 58
      internal/server/wsroute.go
  7. 31
      internal/service/auth.go
  8. 4
      internal/service/user.go
  9. 6
      internal/session/net_client.go

21
api/operation.go

@ -5,9 +5,10 @@ import (
"fmt"
"github.com/golang/protobuf/proto"
"reflect"
"strings"
)
const opOffset int32 = 13578
const OpOffset int32 = 13578
// 注册消息类型
var protoInstances = []proto.Message{
@ -21,6 +22,7 @@ var protoInstances = []proto.Message{
var (
prototypeOpMap = make(map[reflect.Type]int32)
opPrototypeMap = make(map[int32]reflect.Type) // TODO array
opProtoNameMap = make(map[int32][2]string)
// opInstanceMap = make(map[int]any)
)
@ -28,9 +30,17 @@ func init() {
for i, defaultInstance := range protoInstances {
op := int32(i)
t := reflect.TypeOf(defaultInstance)
prototypeOpMap[t] = op + opOffset
prototypeOpMap[t] = op + OpOffset
opPrototypeMap[op] = t
// opInstanceMap[i] = defaultInstance
pkg := t.Elem().PkgPath()
idx := strings.LastIndex(pkg, "/")
if idx != -1 {
pkg = pkg[idx+1:]
}
name := t.Elem().Name()
opProtoNameMap[op+OpOffset] = [2]string{pkg, name}
}
}
@ -44,10 +54,15 @@ func GetProtoOp(msg any) (int32, error) {
}
func NewProtoInstance(op int32) (proto.Message, error) {
prototype := opPrototypeMap[op-opOffset]
prototype := opPrototypeMap[op-OpOffset]
if prototype == nil {
return nil, errors.New(fmt.Sprintf("op :%d not registry!", op))
}
val := reflect.New(prototype.Elem())
return val.Interface().(proto.Message), nil
}
// GetOpNameMap 获取编号和消息
func GetOpNameMap() map[int32][2]string {
return opProtoNameMap
}

2
api/poker.pb.go

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.17.3
// protoc v3.21.12
// source: poker.proto
package api

9
etc/config.toml

@ -5,10 +5,11 @@ addr = ":9999"
[auth]
aesTokenKey = "9Nz3Y6DES3msAFndz4QsJAECUIFkf+KKaRa+jRnNALk="
ignoreUrl = [
"/ws",
"/auth/captcha",
"/auth/authorize",
"/user/findByName",
"/api/conn/ws",
"/api/conn/opMap",
"/api/auth/captcha",
"/api/auth/authorize",
"/api/user/findByName",
]
# 需要输入图形验证码的url
captchaUrl = [

14
internal/server/server.go

@ -2,6 +2,8 @@ package server
import (
"github.com/gin-gonic/gin"
"net/http"
"texas-poker-bk/api"
"texas-poker-bk/internal/service"
)
@ -13,14 +15,20 @@ func NewServer() *gin.Engine {
// session storage
server.Use(SessionStore("golang-tech-stack"))
base := server.Group("/api")
conn := base.Group("/conn")
// 消息映射关系 TODO 生成 json 放在前端
conn.GET("/opMap", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"data": gin.H{"offset": api.OpOffset, "opMap": api.GetOpNameMap()}})
})
// websocket
server.GET("/ws", Upgrade)
conn.GET("/ws", Upgrade)
auth := server.Group("/auth")
auth := base.Group("/auth")
auth.GET("/captcha", service.Captcha) // 验证码
auth.POST("/authorize", service.Authorize) // 登录或注册认证
user := server.Group("/user")
user := base.Group("/user")
user.GET("/findByName", service.FindUserByName)
return server

42
internal/server/ws.go

@ -83,24 +83,34 @@ func handleNetClient(client *session.NetClient) {
client.Close("api body unmarshal fail! " + err.Error())
}
// TODO queue channel -> msg -> handler
var called bool
var res proto.Message
var resErr error
handleNetClient := NetClientHandlers[wrap.Op]
if handleNetClient != nil {
handleNetClient(client, msg)
continue
if called = handleNetClient != nil; called {
res, resErr = handleNetClient(client, msg)
// continue
} else {
// account process
handlerNetAccount := NetAccountHandlers[wrap.Op]
if called = handlerNetAccount != nil; called {
// TODO account check
res, resErr = handlerNetAccount(client.Account, msg)
// continue
} else {
// player process
handlerPlayer := PlayerHandlers[wrap.Op]
if called = handlerPlayer != nil; called {
// TODO player check
res, resErr = handlerPlayer(nil, msg)
}
}
}
// account process
handlerNetAccount := NetAccountHandlers[wrap.Op]
if handlerNetAccount != nil {
// TODO account check
handlerNetAccount(client.Account, msg)
continue
}
// player process
handlerPlayer := PlayerHandlers[wrap.Op]
if handlerPlayer != nil {
// TODO player check
handlerPlayer(nil, msg)
continue
// TODO 处理请求对应消息响应
fmt.Println(called, res, resErr)
// 写响应
if res != nil {
client.WriteSeq(wrap.Seq, res)
}
// log not found handler wrap.Op

58
internal/server/wsroute.go

@ -5,13 +5,29 @@ import (
"reflect"
"texas-poker-bk/api"
"texas-poker-bk/internal/service"
"texas-poker-bk/internal/session"
)
var NetClientHandlers = make(map[int32]func(*session.NetClient, proto.Message))
var NetAccountHandlers = make(map[int32]func(*session.NetAccount, proto.Message))
var PlayerHandlers = make(map[int32]func(*session.Player, proto.Message))
var NetClientHandlers = make(map[int32]func(*session.NetClient, proto.Message) (proto.Message, error))
var NetAccountHandlers = make(map[int32]func(*session.NetAccount, proto.Message) (proto.Message, error))
var PlayerHandlers = make(map[int32]func(*session.Player, proto.Message) (proto.Message, error))
func init() {
HandleNetClientMsg(&api.ReqIdentity{}, func(client *session.NetClient, msg *api.ReqIdentity) (proto.Message, error) {
subject, err := service.DecodeSubject(msg.Token)
if err != nil {
client.Close("authorize failed!")
return nil, nil
}
account := &session.NetAccount{Id: subject.Id, UserName: subject.Name, Client: client}
client.Account = account
// response
res := &api.ResIdentity{Status: 200, Msg: "ok"}
// client.Write(res)
return res, nil
})
}
func checkExistsTypeHandler(op int32, err error) {
if err != nil {
@ -23,42 +39,26 @@ func checkExistsTypeHandler(op int32, err error) {
}
}
func HandleNetClientMsg[T proto.Message](msg T, f func(*session.NetClient, T)) {
func HandleNetClientMsg[T proto.Message](msg T, f func(*session.NetClient, T) (proto.Message, error)) {
op, err := api.GetProtoOp(msg)
checkExistsTypeHandler(op, err)
NetClientHandlers[op] = func(client *session.NetClient, msg proto.Message) {
f(client, msg.(T))
NetClientHandlers[op] = func(client *session.NetClient, msg proto.Message) (proto.Message, error) {
return f(client, msg.(T))
}
}
func HandleNetAccountMsg[T proto.Message](msg T, f func(*session.NetAccount, T)) {
func HandleNetAccountMsg[T proto.Message](msg T, f func(*session.NetAccount, T) (proto.Message, error)) {
op, err := api.GetProtoOp(msg)
checkExistsTypeHandler(op, err)
NetAccountHandlers[op] = func(account *session.NetAccount, msg proto.Message) {
f(account, msg.(T))
NetAccountHandlers[op] = func(account *session.NetAccount, msg proto.Message) (proto.Message, error) {
return f(account, msg.(T))
}
}
func HandleNetPlayerMsg[T proto.Message](msg T, f func(*session.Player, T)) {
func HandleNetPlayerMsg[T proto.Message](msg T, f func(*session.Player, T) (proto.Message, error)) {
op, err := api.GetProtoOp(msg)
checkExistsTypeHandler(op, err)
PlayerHandlers[op] = func(player *session.Player, msg proto.Message) {
f(player, msg.(T))
PlayerHandlers[op] = func(player *session.Player, msg proto.Message) (proto.Message, error) {
return f(player, msg.(T))
}
}
func init() {
HandleNetClientMsg(&api.ReqIdentity{}, func(client *session.NetClient, msg *api.ReqIdentity) {
subject, err := service.DecodeSubject(msg.Token)
if err != nil {
client.Close("authorize failed!")
return
}
account := &session.NetAccount{Id: subject.Id, UserName: subject.Name}
client.Account = account
// response
res := &api.ResIdentity{Status: 200, Msg: "ok"}
client.Write(res)
})
}

31
internal/service/auth.go

@ -3,6 +3,7 @@ package service
import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"texas-poker-bk/internal/conf"
@ -21,6 +22,14 @@ var (
aesTokenKeyBytes []byte // token aes 加密 key
)
func init() {
keyBytes, err := base64.StdEncoding.DecodeString(conf.Conf.Auth.AesTokenKey)
if err != nil {
panic(err)
}
aesTokenKeyBytes = keyBytes
}
// Authorize 登录或注册
func Authorize(ctx *gin.Context) {
username := ctx.PostForm("username")
@ -29,7 +38,7 @@ func Authorize(ctx *gin.Context) {
//username := ctx.Param("username")
//password := ctx.Param("password")
if username == "" || password == "" {
ctx.JSON(http.StatusBadRequest, gin.H{"api": "用户名或密码不能为空"})
ctx.JSON(http.StatusBadRequest, gin.H{"msg": "用户名或密码不能为空"})
return
}
user := userDao.FindUserByName(username)
@ -39,7 +48,7 @@ func Authorize(ctx *gin.Context) {
} else {
// 校验密码
if user.Password != password {
ctx.JSON(http.StatusUnauthorized, gin.H{"api": "用户名或密码有误"})
ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "用户名或密码有误"})
return
}
}
@ -47,7 +56,7 @@ func Authorize(ctx *gin.Context) {
sub := &session.Subject{Id: user.Id, Name: user.Username, Time: time.Now().UnixMilli()}
token, err := EncodeSubject(sub)
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"api": err.Error()})
ctx.JSON(http.StatusUnauthorized, gin.H{"msg": err.Error()})
return
}
ctx.JSON(http.StatusOK, gin.H{"data": gin.H{"token": token}})
@ -65,14 +74,6 @@ func registerUser(username string, password string) *entity.User {
return user
}
func init() {
keyBytes, err := base64.StdEncoding.DecodeString(conf.Conf.Auth.AesTokenKey)
if err != nil {
panic(err)
}
aesTokenKeyBytes = keyBytes
}
// SubjectAuthFilter 认证过滤器
func SubjectAuthFilter(ctx *gin.Context) {
@ -88,14 +89,16 @@ func SubjectAuthFilter(ctx *gin.Context) {
auth := ctx.GetHeader("Authorization")
if auth == "" {
ctx.JSON(http.StatusUnauthorized, gin.H{"api": "请登录后进行操作"})
ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "请登录后进行操作"})
ctx.Abort()
return
}
defer func() {
// 捕获aes解析错误
if r := recover(); r != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"api": "认证失败,请重新登录"})
fmt.Println("request error:", r)
ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "认证失败,请重新登录"})
ctx.Abort()
}
}()
@ -103,7 +106,7 @@ func SubjectAuthFilter(ctx *gin.Context) {
// 这里返回 error,不然后捕获后续执行 handler 的 panic
subject, err := DecodeSubject(auth)
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"api": "认证失败,请重新登录"})
ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "认证失败,请重新登录"})
ctx.Abort()
} else {
// 设置token用户到请求上下文

4
internal/service/user.go

@ -27,12 +27,12 @@ func FindUserByName(ctx *gin.Context) {
user := userDao.FindUserByName(username)
if user == nil {
ctx.JSON(http.StatusOK, gin.H{
"api": "not found",
"msg": "not found",
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"api": "ok",
"msg": "ok",
"data": user,
})
}

6
internal/session/net_client.go

@ -22,12 +22,16 @@ func (c *NetClient) Close(reason string) {
}
func (c *NetClient) Write(msg proto.Message) {
c.WriteSeq(0, msg)
}
func (c *NetClient) WriteSeq(seq int32, msg proto.Message) {
op, err := api.GetProtoOp(msg)
if err != nil {
return
}
bytes, err := proto.Marshal(msg)
wrap := &api.ProtoWrap{Ver: 1, Op: op, Seq: 1, Body: bytes}
wrap := &api.ProtoWrap{Ver: 1, Op: op, Seq: seq, Body: bytes}
wrapBytes, err := proto.Marshal(wrap)
err = c.Conn.WriteMessage(websocket.BinaryMessage, wrapBytes)
}

Loading…
Cancel
Save