mirror of
https://github.com/kataras/iris.git
synced 2025-03-14 08:26:26 +01:00
Websocket additions
Former-commit-id: 662bdd01a7cd403d1f7b8f0d17bed16ed1f06562
This commit is contained in:
parent
7533e2bf8b
commit
4e1d64c5c0
|
@ -23,6 +23,8 @@ to adapt the new changes to your application, it contains an overview of the new
|
|||
- Developers can use a `yaml` files for the configuration using the `iris.YAML` function: `app := iris.New(iris.YAML("myconfiguration.yaml"))`
|
||||
- Add `.Regex` middleware which does path validation using the `regexp` package, i.e `.Regex("param", "[0-9]+$")`. Useful for routers that don't support regex route path validation out-of-the-box.
|
||||
|
||||
- Websocket additions: `c.Context() *iris.Context`, `ws.GetConnectionsByRoom("room name") []websocket.Connection`, `c.OnLeave(func(roomName string){})`, `c.Values().Set(key,value)/.Get(key).Reset()` (where ws:websocket.Server insance, where c:websocket.Connection instance)
|
||||
|
||||
Fixes:
|
||||
|
||||
- Websocket improvements and fix errors when using custom golang client
|
||||
|
|
|
@ -93,7 +93,7 @@ var Ws = (function () {
|
|||
m = JSON.stringify(data);
|
||||
}
|
||||
else {
|
||||
console.log("Invalid");
|
||||
console.log("Invalid, javascript-side should contains an empty second parameter.");
|
||||
}
|
||||
return this._msg(event, t, m);
|
||||
};
|
||||
|
|
|
@ -113,7 +113,7 @@ class Ws {
|
|||
t = websocketJSONMessageType;
|
||||
m = JSON.stringify(data);
|
||||
} else {
|
||||
console.log("Invalid");
|
||||
console.log("Invalid, javascript-side should contains an empty second parameter.");
|
||||
}
|
||||
|
||||
return this._msg(event, t, m);
|
||||
|
|
|
@ -12,6 +12,61 @@ import (
|
|||
"gopkg.in/kataras/iris.v6"
|
||||
)
|
||||
|
||||
type (
|
||||
connectionValue struct {
|
||||
key []byte
|
||||
value interface{}
|
||||
}
|
||||
// ConnectionValues is the temporary connection's memory store
|
||||
ConnectionValues []connectionValue
|
||||
)
|
||||
|
||||
// Set sets a value based on the key
|
||||
func (r *ConnectionValues) Set(key string, value interface{}) {
|
||||
args := *r
|
||||
n := len(args)
|
||||
for i := 0; i < n; i++ {
|
||||
kv := &args[i]
|
||||
if string(kv.key) == key {
|
||||
kv.value = value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c := cap(args)
|
||||
if c > n {
|
||||
args = args[:n+1]
|
||||
kv := &args[n]
|
||||
kv.key = append(kv.key[:0], key...)
|
||||
kv.value = value
|
||||
*r = args
|
||||
return
|
||||
}
|
||||
|
||||
kv := connectionValue{}
|
||||
kv.key = append(kv.key[:0], key...)
|
||||
kv.value = value
|
||||
*r = append(args, kv)
|
||||
}
|
||||
|
||||
// Get returns a value based on its key
|
||||
func (r *ConnectionValues) Get(key string) interface{} {
|
||||
args := *r
|
||||
n := len(args)
|
||||
for i := 0; i < n; i++ {
|
||||
kv := &args[i]
|
||||
if string(kv.key) == key {
|
||||
return kv.value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset clears the values
|
||||
func (r *ConnectionValues) Reset() {
|
||||
*r = (*r)[:0]
|
||||
}
|
||||
|
||||
// UnderlineConnection is used for compatible with fasthttp and net/http underline websocket libraries
|
||||
// we only need ~8 funcs from websocket.Conn so:
|
||||
type UnderlineConnection interface {
|
||||
|
@ -65,6 +120,10 @@ type UnderlineConnection interface {
|
|||
type (
|
||||
// DisconnectFunc is the callback which fires when a client/connection closed
|
||||
DisconnectFunc func()
|
||||
// LeaveRoomFunc is the callback which fires when a client/connection leaves from any room.
|
||||
// This is called automatically when client/connection disconnected
|
||||
// (because websocket server automatically leaves from all joined rooms)
|
||||
LeaveRoomFunc func(roomName string)
|
||||
// ErrorFunc is the callback which fires when an error happens
|
||||
ErrorFunc (func(string))
|
||||
// NativeMessageFunc is the callback for native websocket messages, receives one []byte parameter which is the raw client's message
|
||||
|
@ -84,6 +143,8 @@ type (
|
|||
// websocket has everything you need to authenticate the user BUT if it's necessary
|
||||
// then you use it to receive user information, for example: from headers
|
||||
Context() *iris.Context
|
||||
// Values returns the temporary lock-free connection's data store
|
||||
Values() ConnectionValues
|
||||
|
||||
// OnDisconnect registers a callback which fires when this connection is closed by an error or manual
|
||||
OnDisconnect(DisconnectFunc)
|
||||
|
@ -103,7 +164,15 @@ type (
|
|||
// Join join a connection to a room, it doesn't check if connection is already there, so care
|
||||
Join(string)
|
||||
// Leave removes a connection from a room
|
||||
Leave(string)
|
||||
// Returns true if the connection has actually left from the particular room.
|
||||
Leave(string) bool
|
||||
// OnLeave registeres a callback which fires when this connection left from any joined room.
|
||||
// This callback is called automatically on Disconnected client, because websocket server automatically
|
||||
// deletes the disconnected connection from any joined rooms.
|
||||
//
|
||||
// Note: the callback(s) called right before the server deletes the connection from the room
|
||||
// so the connection theoritical can still send messages to its room right before it is being disconnected.
|
||||
OnLeave(roomLeaveCb LeaveRoomFunc)
|
||||
// Disconnect disconnects the client, close the underline websocket conn and removes it from the conn list
|
||||
// returns the error, if any, from the underline connection
|
||||
Disconnect() error
|
||||
|
@ -116,6 +185,7 @@ type (
|
|||
pinger *time.Ticker
|
||||
disconnected bool
|
||||
onDisconnectListeners []DisconnectFunc
|
||||
onRoomLeaveListeners []LeaveRoomFunc
|
||||
onErrorListeners []ErrorFunc
|
||||
onNativeMessageListeners []NativeMessageFunc
|
||||
onEventListeners map[string][]MessageFunc
|
||||
|
@ -126,6 +196,7 @@ type (
|
|||
|
||||
// access to the Context, use with causion, you can't use response writer as you imagine.
|
||||
ctx *iris.Context
|
||||
values ConnectionValues
|
||||
server *server
|
||||
// #119 , websocket writers are not protected by locks inside the gorilla's websocket code
|
||||
// so we must protect them otherwise we're getting concurrent connection error on multi writers in the same time.
|
||||
|
@ -144,6 +215,7 @@ func newConnection(s *server, ctx *iris.Context, underlineConn UnderlineConnecti
|
|||
id: id,
|
||||
messageType: websocket.TextMessage,
|
||||
onDisconnectListeners: make([]DisconnectFunc, 0),
|
||||
onRoomLeaveListeners: make([]LeaveRoomFunc, 0),
|
||||
onErrorListeners: make([]ErrorFunc, 0),
|
||||
onNativeMessageListeners: make([]NativeMessageFunc, 0),
|
||||
onEventListeners: make(map[string][]MessageFunc, 0),
|
||||
|
@ -317,6 +389,10 @@ func (c *connection) Context() *iris.Context {
|
|||
return c.ctx
|
||||
}
|
||||
|
||||
func (c *connection) Values() ConnectionValues {
|
||||
return c.values
|
||||
}
|
||||
|
||||
func (c *connection) fireDisconnect() {
|
||||
for i := range c.onDisconnectListeners {
|
||||
c.onDisconnectListeners[i]()
|
||||
|
@ -373,8 +449,20 @@ func (c *connection) Join(roomName string) {
|
|||
c.server.Join(roomName, c.id)
|
||||
}
|
||||
|
||||
func (c *connection) Leave(roomName string) {
|
||||
c.server.Leave(roomName, c.id)
|
||||
func (c *connection) Leave(roomName string) bool {
|
||||
return c.server.Leave(roomName, c.id)
|
||||
}
|
||||
|
||||
func (c *connection) OnLeave(roomLeaveCb LeaveRoomFunc) {
|
||||
c.onRoomLeaveListeners = append(c.onRoomLeaveListeners, roomLeaveCb)
|
||||
// note: the callbacks are called from the server on the '.leave' and '.LeaveAll' funcs.
|
||||
}
|
||||
|
||||
func (c *connection) fireOnLeave(roomName string) {
|
||||
// fire the onRoomLeaveListeners
|
||||
for i := range c.onRoomLeaveListeners {
|
||||
c.onRoomLeaveListeners[i](roomName)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connection) Disconnect() error {
|
||||
|
|
|
@ -48,7 +48,12 @@ type Server interface {
|
|||
// first parameter is the room name and the second the connection.ID()
|
||||
//
|
||||
// You can use connection.Leave("room name") instead.
|
||||
Leave(roomName string, connID string)
|
||||
// Returns true if the connection has actually left from the particular room.
|
||||
Leave(roomName string, connID string) bool
|
||||
|
||||
// GetConnectionsByRoom returns a list of Connection
|
||||
// are joined to this room.
|
||||
GetConnectionsByRoom(roomName string) []Connection
|
||||
|
||||
// Disconnect force-disconnects a websocket connection
|
||||
// based on its connection.ID()
|
||||
|
@ -282,6 +287,8 @@ func (s *server) LeaveAll(connID string) {
|
|||
for name, connectionIDs := range s.rooms {
|
||||
for i := range connectionIDs {
|
||||
if connectionIDs[i] == connID {
|
||||
// fire the on room leave connection's listeners
|
||||
s.connections.get(connID).fireOnLeave(name)
|
||||
// the connection is inside this room, lets remove it
|
||||
s.rooms[name][i] = s.rooms[name][len(s.rooms[name])-1]
|
||||
s.rooms[name] = s.rooms[name][:len(s.rooms[name])-1]
|
||||
|
@ -295,14 +302,16 @@ func (s *server) LeaveAll(connID string) {
|
|||
// first parameter is the room name and the second the connection.ID()
|
||||
//
|
||||
// You can use connection.Leave("room name") instead.
|
||||
func (s *server) Leave(roomName string, connID string) {
|
||||
// Returns true if the connection has actually left from the particular room.
|
||||
func (s *server) Leave(roomName string, connID string) bool {
|
||||
s.mu.Lock()
|
||||
s.leave(roomName, connID)
|
||||
left := s.leave(roomName, connID)
|
||||
s.mu.Unlock()
|
||||
return left
|
||||
}
|
||||
|
||||
// leave used internally, no locks used.
|
||||
func (s *server) leave(roomName string, connID string) {
|
||||
func (s *server) leave(roomName string, connID string) (left bool) {
|
||||
///THINK: we could add locks to its room but we still use the lock for the whole rooms or we can just do what we do with connections
|
||||
// I will think about it on the next revision, so far we use the locks only for rooms so we are ok...
|
||||
if s.rooms[roomName] != nil {
|
||||
|
@ -310,6 +319,7 @@ func (s *server) leave(roomName string, connID string) {
|
|||
if s.rooms[roomName][i] == connID {
|
||||
s.rooms[roomName][i] = s.rooms[roomName][len(s.rooms[roomName])-1]
|
||||
s.rooms[roomName] = s.rooms[roomName][:len(s.rooms[roomName])-1]
|
||||
left = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -317,6 +327,27 @@ func (s *server) leave(roomName string, connID string) {
|
|||
delete(s.rooms, roomName)
|
||||
}
|
||||
}
|
||||
|
||||
if left {
|
||||
// fire the on room leave connection's listeners
|
||||
s.connections.get(connID).fireOnLeave(roomName)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetConnectionsByRoom returns a list of Connection
|
||||
// which are joined to this room.
|
||||
func (s *server) GetConnectionsByRoom(roomName string) []Connection {
|
||||
s.mu.Lock()
|
||||
var conns []Connection
|
||||
if connIDs, found := s.rooms[roomName]; found {
|
||||
for _, connID := range connIDs {
|
||||
conns = append(conns, s.connections.get(connID))
|
||||
}
|
||||
|
||||
}
|
||||
s.mu.Unlock()
|
||||
return conns
|
||||
}
|
||||
|
||||
// emitMessage is the main 'router' of the messages coming from the connection
|
||||
|
@ -369,14 +400,17 @@ func (s *server) emitMessage(from, to string, data []byte) {
|
|||
//
|
||||
// You can use the connection.Disconnect() instead.
|
||||
func (s *server) Disconnect(connID string) (err error) {
|
||||
// leave from all joined rooms before remove the actual connection from the list.
|
||||
// note: we cannot use that to send data if the client is actually closed.
|
||||
s.LeaveAll(connID)
|
||||
|
||||
// remove the connection from the list
|
||||
if c, ok := s.connections.remove(connID); ok {
|
||||
if !c.disconnected {
|
||||
c.disconnected = true
|
||||
// stop the ping timer
|
||||
c.pinger.Stop()
|
||||
// leave from all joined rooms
|
||||
s.LeaveAll(connID)
|
||||
|
||||
// fire the disconnect callbacks, if any
|
||||
c.fireDisconnect()
|
||||
// close the underline connection and return its error, if any.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Package websocket provides an easy way to setup server and client side rich websocket experience for Iris
|
||||
// As originally written by me at https://github.com/kataras/go-websocket
|
||||
// As originally written by me at https://github.com/kataras/go-websocket based on v0.1.1
|
||||
package websocket
|
||||
|
||||
import (
|
||||
|
|
Loading…
Reference in New Issue
Block a user