136 lines
2.6 KiB
Go
136 lines
2.6 KiB
Go
package socketio
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sync"
|
|
|
|
"github.com/googollee/go-socket.io/parser"
|
|
)
|
|
|
|
// Namespace describes a communication channel that allows you to split the logic of your application
|
|
// over a single shared connection.
|
|
type Namespace interface {
|
|
// Context of this connection. You can save one context for one
|
|
// connection, and share it between all handlers. The handlers
|
|
// are called in one goroutine, so no need to lock context if it
|
|
// only accessed in one connection.
|
|
Context() interface{}
|
|
SetContext(ctx interface{})
|
|
|
|
Namespace() string
|
|
Emit(eventName string, v ...interface{})
|
|
|
|
Join(room string)
|
|
Leave(room string)
|
|
LeaveAll()
|
|
Rooms() []string
|
|
}
|
|
|
|
type namespaceConn struct {
|
|
*conn
|
|
broadcast Broadcast
|
|
|
|
namespace string
|
|
context interface{}
|
|
|
|
ack sync.Map
|
|
}
|
|
|
|
func newNamespaceConn(conn *conn, namespace string, broadcast Broadcast) *namespaceConn {
|
|
return &namespaceConn{
|
|
conn: conn,
|
|
namespace: namespace,
|
|
broadcast: broadcast,
|
|
}
|
|
}
|
|
|
|
func (nc *namespaceConn) SetContext(ctx interface{}) {
|
|
nc.context = ctx
|
|
}
|
|
|
|
func (nc *namespaceConn) Context() interface{} {
|
|
return nc.context
|
|
}
|
|
|
|
func (nc *namespaceConn) Namespace() string {
|
|
return nc.namespace
|
|
}
|
|
|
|
func (nc *namespaceConn) Emit(eventName string, v ...interface{}) {
|
|
header := parser.Header{
|
|
Type: parser.Event,
|
|
}
|
|
|
|
if nc.namespace != aliasRootNamespace {
|
|
header.Namespace = nc.namespace
|
|
}
|
|
|
|
if l := len(v); l > 0 {
|
|
last := v[l-1]
|
|
lastV := reflect.TypeOf(last)
|
|
|
|
if lastV.Kind() == reflect.Func {
|
|
f := newAckFunc(last)
|
|
|
|
header.ID = nc.conn.nextID()
|
|
header.NeedAck = true
|
|
|
|
nc.ack.Store(header.ID, f)
|
|
v = v[:l-1]
|
|
}
|
|
}
|
|
|
|
args := make([]reflect.Value, len(v)+1)
|
|
args[0] = reflect.ValueOf(eventName)
|
|
|
|
for i := 1; i < len(args); i++ {
|
|
args[i] = reflect.ValueOf(v[i-1])
|
|
}
|
|
|
|
nc.conn.write(header, args...)
|
|
}
|
|
|
|
func (nc *namespaceConn) Join(room string) {
|
|
nc.broadcast.Join(room, nc)
|
|
}
|
|
|
|
func (nc *namespaceConn) Leave(room string) {
|
|
nc.broadcast.Leave(room, nc)
|
|
}
|
|
|
|
func (nc *namespaceConn) LeaveAll() {
|
|
nc.broadcast.LeaveAll(nc)
|
|
}
|
|
|
|
func (nc *namespaceConn) Rooms() []string {
|
|
return nc.broadcast.Rooms(nc)
|
|
}
|
|
|
|
func (nc *namespaceConn) dispatch(header parser.Header) {
|
|
if header.Type != parser.Ack {
|
|
return
|
|
}
|
|
|
|
rawFunc, ok := nc.ack.Load(header.ID)
|
|
if ok {
|
|
f, ok := rawFunc.(*funcHandler)
|
|
if !ok {
|
|
nc.conn.onError(nc.namespace, fmt.Errorf("incorrect data stored for header %d", header.ID))
|
|
return
|
|
}
|
|
|
|
nc.ack.Delete(header.ID)
|
|
|
|
args, err := nc.conn.parseArgs(f.argTypes)
|
|
if err != nil {
|
|
nc.conn.onError(nc.namespace, err)
|
|
return
|
|
}
|
|
if _, err := f.Call(args); err != nil {
|
|
nc.conn.onError(nc.namespace, err)
|
|
return
|
|
}
|
|
}
|
|
}
|