gosocketio/vendor/github.com/googollee/go-socket.io/server.go

342 lines
7.1 KiB
Go

package socketio
import (
"errors"
"net/http"
"github.com/gomodule/redigo/redis"
"github.com/googollee/go-socket.io/engineio"
"github.com/googollee/go-socket.io/parser"
)
// Server is a go-socket.io server.
type Server struct {
engine *engineio.Server
handlers *namespaceHandlers
redisAdapter *RedisAdapterOptions
}
// NewServer returns a server.
func NewServer(opts *engineio.Options) *Server {
return &Server{
handlers: newNamespaceHandlers(),
engine: engineio.NewServer(opts),
}
}
// Adapter sets redis broadcast adapter.
func (s *Server) Adapter(opts *RedisAdapterOptions) (bool, error) {
opts = getOptions(opts)
var redisOpts []redis.DialOption
if len(opts.Password) > 0 {
redisOpts = append(redisOpts, redis.DialPassword(opts.Password))
}
if opts.DB > 0 {
redisOpts = append(redisOpts, redis.DialDatabase(opts.DB))
}
conn, err := redis.Dial(opts.Network, opts.getAddr(), redisOpts...)
if err != nil {
return false, err
}
s.redisAdapter = opts
conn.Close()
return true, nil
}
// Close closes server.
func (s *Server) Close() error {
return s.engine.Close()
}
// ServeHTTP dispatches the request to the handler whose pattern most closely matches the request URL.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.engine.ServeHTTP(w, r)
}
// OnConnect set a handler function f to handle open event for namespace.
func (s *Server) OnConnect(namespace string, f func(Conn) error) {
h := s.getNamespace(namespace)
if h == nil {
h = s.createNamespace(namespace)
}
h.OnConnect(f)
}
// OnDisconnect set a handler function f to handle disconnect event for namespace.
func (s *Server) OnDisconnect(namespace string, f func(Conn, string)) {
h := s.getNamespace(namespace)
if h == nil {
h = s.createNamespace(namespace)
}
h.OnDisconnect(f)
}
// OnError set a handler function f to handle error for namespace.
func (s *Server) OnError(namespace string, f func(Conn, error)) {
h := s.getNamespace(namespace)
if h == nil {
h = s.createNamespace(namespace)
}
h.OnError(f)
}
// OnEvent set a handler function f to handle event for namespace.
func (s *Server) OnEvent(namespace, event string, f interface{}) {
h := s.getNamespace(namespace)
if h == nil {
h = s.createNamespace(namespace)
}
h.OnEvent(event, f)
}
// Serve serves go-socket.io server.
func (s *Server) Serve() error {
for {
conn, err := s.engine.Accept()
//todo maybe need check EOF from Accept()
if err != nil {
return err
}
go s.serveConn(conn)
}
}
// JoinRoom joins given connection to the room.
func (s *Server) JoinRoom(namespace string, room string, connection Conn) bool {
nspHandler := s.getNamespace(namespace)
if nspHandler != nil {
nspHandler.broadcast.Join(room, connection)
return true
}
return false
}
// LeaveRoom leaves given connection from the room.
func (s *Server) LeaveRoom(namespace string, room string, connection Conn) bool {
nspHandler := s.getNamespace(namespace)
if nspHandler != nil {
nspHandler.broadcast.Leave(room, connection)
return true
}
return false
}
// LeaveAllRooms leaves the given connection from all rooms.
func (s *Server) LeaveAllRooms(namespace string, connection Conn) bool {
nspHandler := s.getNamespace(namespace)
if nspHandler != nil {
nspHandler.broadcast.LeaveAll(connection)
return true
}
return false
}
// ClearRoom clears the room.
func (s *Server) ClearRoom(namespace string, room string) bool {
nspHandler := s.getNamespace(namespace)
if nspHandler != nil {
nspHandler.broadcast.Clear(room)
return true
}
return false
}
// BroadcastToRoom broadcasts given event & args to all the connections in the room.
func (s *Server) BroadcastToRoom(namespace string, room, event string, args ...interface{}) bool {
nspHandler := s.getNamespace(namespace)
if nspHandler != nil {
nspHandler.broadcast.Send(room, event, args...)
return true
}
return false
}
// BroadcastToNamespace broadcasts given event & args to all the connections in the same namespace.
func (s *Server) BroadcastToNamespace(namespace string, event string, args ...interface{}) bool {
nspHandler := s.getNamespace(namespace)
if nspHandler != nil {
nspHandler.broadcast.SendAll(event, args...)
return true
}
return false
}
// RoomLen gives number of connections in the room.
func (s *Server) RoomLen(namespace string, room string) int {
nspHandler := s.getNamespace(namespace)
if nspHandler != nil {
return nspHandler.broadcast.Len(room)
}
return -1
}
// Rooms gives list of all the rooms.
func (s *Server) Rooms(namespace string) []string {
nspHandler := s.getNamespace(namespace)
if nspHandler != nil {
return nspHandler.broadcast.Rooms(nil)
}
return nil
}
// Count number of connections.
func (s *Server) Count() int {
return s.engine.Count()
}
// ForEach sends data by DataFunc, if room does not exit sends anything.
func (s *Server) ForEach(namespace string, room string, f EachFunc) bool {
nspHandler := s.getNamespace(namespace)
if nspHandler != nil {
nspHandler.broadcast.ForEach(room, f)
return true
}
return false
}
func (s *Server) serveConn(conn engineio.Conn) {
c := newConn(conn, s.handlers)
if err := c.connect(); err != nil {
_ = c.Close()
if root, ok := s.handlers.Get(rootNamespace); ok && root.onError != nil {
root.onError(nil, err)
}
return
}
go s.serveError(c)
go s.serveWrite(c)
go s.serveRead(c)
}
func (s *Server) serveError(c *conn) {
defer func() {
c.Close()
s.engine.Remove(c.ID())
}()
for {
select {
case <-c.quitChan:
return
case err := <-c.errorChan:
var errMsg *errorMessage
if !errors.As(err, &errMsg) {
continue
}
if handler := c.namespace(errMsg.namespace); handler != nil {
if handler.onError != nil {
nsConn, ok := c.namespaces.Get(errMsg.namespace)
if !ok {
continue
}
handler.onError(nsConn, errMsg.err)
}
}
}
}
}
func (s *Server) serveWrite(c *conn) {
defer func() {
c.Close()
s.engine.Remove(c.ID())
}()
for {
select {
case <-c.quitChan:
return
case pkg := <-c.writeChan:
if err := c.encoder.Encode(pkg.Header, pkg.Data); err != nil {
c.onError(pkg.Header.Namespace, err)
}
}
}
}
func (s *Server) serveRead(c *conn) {
defer func() {
c.Close()
s.engine.Remove(c.ID())
}()
var event string
for {
var header parser.Header
if err := c.decoder.DecodeHeader(&header, &event); err != nil {
c.onError(rootNamespace, err)
return
}
if header.Namespace == aliasRootNamespace {
header.Namespace = rootNamespace
}
var err error
switch header.Type {
case parser.Ack, parser.Connect, parser.Disconnect:
handler, ok := readHandlerMapping[header.Type]
if !ok {
return
}
err = handler(c, header)
case parser.Event:
err = eventPacketHandler(c, event, header)
}
if err != nil {
return
}
}
}
func (s *Server) createNamespace(nsp string) *namespaceHandler {
if nsp == aliasRootNamespace {
nsp = rootNamespace
}
handler := newNamespaceHandler(nsp, s.redisAdapter)
s.handlers.Set(nsp, handler)
return handler
}
func (s *Server) getNamespace(nsp string) *namespaceHandler {
if nsp == aliasRootNamespace {
nsp = rootNamespace
}
ret, ok := s.handlers.Get(nsp)
if !ok {
return nil
}
return ret
}