clientctx.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Client handler
  2. package wsrpc
  3. import (
  4. "errors"
  5. "log"
  6. "reflect"
  7. "strings"
  8. "sync"
  9. "github.com/google/uuid"
  10. "golang.org/x/net/websocket"
  11. )
  12. var (
  13. ErrNParam = errors.New("Invalid number of parameters")
  14. ErrNReturn = errors.New("Number of outputs mismatch")
  15. ErrBadImplementation = errors.New("Bad implementation")
  16. errorInterface = reflect.TypeOf((*error)(nil)).Elem()
  17. )
  18. // Call object
  19. type CallObj struct {
  20. OP string `json:"op"` // operation
  21. ID string `json:"id"` // Id of the call
  22. Method string `json:"method"` // could be param 0 instead?
  23. Params []interface{} `json:"params"`
  24. Response interface{} `json:"response"`
  25. }
  26. // DataObj common structure for dymanic json object
  27. //type DataObj map[string]interface{}
  28. // ListenerFunc function type to handle browser events
  29. type ListenerFunc func(...interface{}) (interface{}, error)
  30. // Request request type for handling requests channels
  31. // ClientCtx main client struct
  32. type ClientCtx struct {
  33. locker sync.Mutex
  34. WS *websocket.Conn
  35. // stuff
  36. listeners map[string]ListenerFunc
  37. requests map[string]chan interface{}
  38. }
  39. //NewHandler creates a new WsRPC client handler
  40. func NewHandler(id string, ws *websocket.Conn) *ClientCtx {
  41. var c = ClientCtx{
  42. WS: ws,
  43. listeners: map[string]ListenerFunc{},
  44. requests: map[string]chan interface{}{},
  45. }
  46. return &c
  47. }
  48. // Process messages
  49. func (c *ClientCtx) Process(data CallObj) {
  50. switch data.OP {
  51. case "call":
  52. params := data.Params
  53. var idn = data.Method
  54. var reqID = data.ID
  55. var fn, ok = c.listeners[idn]
  56. if !ok {
  57. return
  58. }
  59. go func() { // async send
  60. ret, err := fn(params...)
  61. if err != nil {
  62. log.Println("Create error response, panic?")
  63. }
  64. var responseObj = CallObj{
  65. OP: "response",
  66. ID: reqID,
  67. Response: ret,
  68. }
  69. //log.Println("Sending response")
  70. websocket.JSON.Send(c.WS, responseObj)
  71. }()
  72. case "response":
  73. c.locker.Lock()
  74. mchan, ok := c.requests[data.ID]
  75. delete(c.requests, data.ID)
  76. c.locker.Unlock()
  77. if ok {
  78. mchan <- data.Response
  79. }
  80. }
  81. }
  82. // Call a client method and estabilishes a request id
  83. func (c *ClientCtx) Call(method string, params ...interface{}) interface{} {
  84. u := uuid.New()
  85. uuidStr := u.String()
  86. var callObj = CallObj{
  87. OP: "call",
  88. ID: uuidStr,
  89. Method: method,
  90. Params: params,
  91. }
  92. res := make(chan interface{}, 1)
  93. c.locker.Lock()
  94. c.requests[uuidStr] = res
  95. c.locker.Unlock()
  96. websocket.JSON.Send(c.WS, &callObj)
  97. return <-res // Block until value
  98. }
  99. //On add a event listener on browser
  100. func (c *ClientCtx) Define(name string, listener ListenerFunc) {
  101. c.listeners[name] = listener
  102. }
  103. func (c *ClientCtx) Export(obj interface{}) {
  104. // Reflect here
  105. typ := reflect.TypeOf(obj)
  106. val := reflect.ValueOf(obj)
  107. for i := 0; i < typ.NumField(); i++ {
  108. fTyp := typ.Field(i)
  109. if fTyp.Type.Kind() != reflect.Func {
  110. log.Println("Ignore non func value")
  111. continue
  112. }
  113. tag, ok := fTyp.Tag.Lookup("wsexport")
  114. if !ok {
  115. continue
  116. }
  117. tagParts := strings.Split(tag, ",")
  118. exportName := tagParts[0]
  119. fnVal := val.Field(i)
  120. // Slow?
  121. c.listeners[exportName] = func(params ...interface{}) (interface{}, error) {
  122. if len(params) != fnVal.Type().NumIn() {
  123. return nil, ErrNParam
  124. }
  125. if fnVal.Type().NumOut() != 2 {
  126. return nil, ErrNReturn
  127. }
  128. if !fnVal.Type().Out(1).Implements(errorInterface) {
  129. return nil, ErrBadImplementation
  130. }
  131. vparam := []reflect.Value{}
  132. for _, p := range params {
  133. vparam = append(vparam, reflect.ValueOf(p))
  134. }
  135. r := fnVal.Call(vparam)
  136. return r[0].Interface(), r[1].Interface().(error)
  137. }
  138. // It is a func, check for tag
  139. }
  140. }