mirror of
https://github.com/kataras/iris.git
synced 2025-03-14 08:16:28 +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"))`
|
- 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.
|
- 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:
|
Fixes:
|
||||||
|
|
||||||
- Websocket improvements and fix errors when using custom golang client
|
- Websocket improvements and fix errors when using custom golang client
|
||||||
|
|
|
@ -93,7 +93,7 @@ var Ws = (function () {
|
||||||
m = JSON.stringify(data);
|
m = JSON.stringify(data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("Invalid");
|
console.log("Invalid, javascript-side should contains an empty second parameter.");
|
||||||
}
|
}
|
||||||
return this._msg(event, t, m);
|
return this._msg(event, t, m);
|
||||||
};
|
};
|
||||||
|
|
|
@ -113,7 +113,7 @@ class Ws {
|
||||||
t = websocketJSONMessageType;
|
t = websocketJSONMessageType;
|
||||||
m = JSON.stringify(data);
|
m = JSON.stringify(data);
|
||||||
} else {
|
} else {
|
||||||
console.log("Invalid");
|
console.log("Invalid, javascript-side should contains an empty second parameter.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._msg(event, t, m);
|
return this._msg(event, t, m);
|
||||||
|
|
|
@ -12,6 +12,61 @@ import (
|
||||||
"gopkg.in/kataras/iris.v6"
|
"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
|
// UnderlineConnection is used for compatible with fasthttp and net/http underline websocket libraries
|
||||||
// we only need ~8 funcs from websocket.Conn so:
|
// we only need ~8 funcs from websocket.Conn so:
|
||||||
type UnderlineConnection interface {
|
type UnderlineConnection interface {
|
||||||
|
@ -65,6 +120,10 @@ type UnderlineConnection interface {
|
||||||
type (
|
type (
|
||||||
// DisconnectFunc is the callback which fires when a client/connection closed
|
// DisconnectFunc is the callback which fires when a client/connection closed
|
||||||
DisconnectFunc func()
|
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 is the callback which fires when an error happens
|
||||||
ErrorFunc (func(string))
|
ErrorFunc (func(string))
|
||||||
// NativeMessageFunc is the callback for native websocket messages, receives one []byte parameter which is the raw client's message
|
// 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
|
// 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
|
// then you use it to receive user information, for example: from headers
|
||||||
Context() *iris.Context
|
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 registers a callback which fires when this connection is closed by an error or manual
|
||||||
OnDisconnect(DisconnectFunc)
|
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 join a connection to a room, it doesn't check if connection is already there, so care
|
||||||
Join(string)
|
Join(string)
|
||||||
// Leave removes a connection from a room
|
// 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
|
// 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
|
// returns the error, if any, from the underline connection
|
||||||
Disconnect() error
|
Disconnect() error
|
||||||
|
@ -116,6 +185,7 @@ type (
|
||||||
pinger *time.Ticker
|
pinger *time.Ticker
|
||||||
disconnected bool
|
disconnected bool
|
||||||
onDisconnectListeners []DisconnectFunc
|
onDisconnectListeners []DisconnectFunc
|
||||||
|
onRoomLeaveListeners []LeaveRoomFunc
|
||||||
onErrorListeners []ErrorFunc
|
onErrorListeners []ErrorFunc
|
||||||
onNativeMessageListeners []NativeMessageFunc
|
onNativeMessageListeners []NativeMessageFunc
|
||||||
onEventListeners map[string][]MessageFunc
|
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.
|
// access to the Context, use with causion, you can't use response writer as you imagine.
|
||||||
ctx *iris.Context
|
ctx *iris.Context
|
||||||
|
values ConnectionValues
|
||||||
server *server
|
server *server
|
||||||
// #119 , websocket writers are not protected by locks inside the gorilla's websocket code
|
// #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.
|
// 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,
|
id: id,
|
||||||
messageType: websocket.TextMessage,
|
messageType: websocket.TextMessage,
|
||||||
onDisconnectListeners: make([]DisconnectFunc, 0),
|
onDisconnectListeners: make([]DisconnectFunc, 0),
|
||||||
|
onRoomLeaveListeners: make([]LeaveRoomFunc, 0),
|
||||||
onErrorListeners: make([]ErrorFunc, 0),
|
onErrorListeners: make([]ErrorFunc, 0),
|
||||||
onNativeMessageListeners: make([]NativeMessageFunc, 0),
|
onNativeMessageListeners: make([]NativeMessageFunc, 0),
|
||||||
onEventListeners: make(map[string][]MessageFunc, 0),
|
onEventListeners: make(map[string][]MessageFunc, 0),
|
||||||
|
@ -317,6 +389,10 @@ func (c *connection) Context() *iris.Context {
|
||||||
return c.ctx
|
return c.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *connection) Values() ConnectionValues {
|
||||||
|
return c.values
|
||||||
|
}
|
||||||
|
|
||||||
func (c *connection) fireDisconnect() {
|
func (c *connection) fireDisconnect() {
|
||||||
for i := range c.onDisconnectListeners {
|
for i := range c.onDisconnectListeners {
|
||||||
c.onDisconnectListeners[i]()
|
c.onDisconnectListeners[i]()
|
||||||
|
@ -373,8 +449,20 @@ func (c *connection) Join(roomName string) {
|
||||||
c.server.Join(roomName, c.id)
|
c.server.Join(roomName, c.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *connection) Leave(roomName string) {
|
func (c *connection) Leave(roomName string) bool {
|
||||||
c.server.Leave(roomName, c.id)
|
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 {
|
func (c *connection) Disconnect() error {
|
||||||
|
|
|
@ -48,7 +48,12 @@ type Server interface {
|
||||||
// first parameter is the room name and the second the connection.ID()
|
// first parameter is the room name and the second the connection.ID()
|
||||||
//
|
//
|
||||||
// You can use connection.Leave("room name") instead.
|
// 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
|
// Disconnect force-disconnects a websocket connection
|
||||||
// based on its connection.ID()
|
// based on its connection.ID()
|
||||||
|
@ -282,6 +287,8 @@ func (s *server) LeaveAll(connID string) {
|
||||||
for name, connectionIDs := range s.rooms {
|
for name, connectionIDs := range s.rooms {
|
||||||
for i := range connectionIDs {
|
for i := range connectionIDs {
|
||||||
if connectionIDs[i] == connID {
|
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
|
// the connection is inside this room, lets remove it
|
||||||
s.rooms[name][i] = s.rooms[name][len(s.rooms[name])-1]
|
s.rooms[name][i] = s.rooms[name][len(s.rooms[name])-1]
|
||||||
s.rooms[name] = 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()
|
// first parameter is the room name and the second the connection.ID()
|
||||||
//
|
//
|
||||||
// You can use connection.Leave("room name") instead.
|
// 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.mu.Lock()
|
||||||
s.leave(roomName, connID)
|
left := s.leave(roomName, connID)
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
return left
|
||||||
}
|
}
|
||||||
|
|
||||||
// leave used internally, no locks used.
|
// 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
|
///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...
|
// 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 {
|
if s.rooms[roomName] != nil {
|
||||||
|
@ -310,6 +319,7 @@ func (s *server) leave(roomName string, connID string) {
|
||||||
if s.rooms[roomName][i] == connID {
|
if s.rooms[roomName][i] == connID {
|
||||||
s.rooms[roomName][i] = s.rooms[roomName][len(s.rooms[roomName])-1]
|
s.rooms[roomName][i] = s.rooms[roomName][len(s.rooms[roomName])-1]
|
||||||
s.rooms[roomName] = s.rooms[roomName][:len(s.rooms[roomName])-1]
|
s.rooms[roomName] = s.rooms[roomName][:len(s.rooms[roomName])-1]
|
||||||
|
left = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,6 +327,27 @@ func (s *server) leave(roomName string, connID string) {
|
||||||
delete(s.rooms, roomName)
|
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
|
// 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.
|
// You can use the connection.Disconnect() instead.
|
||||||
func (s *server) Disconnect(connID string) (err error) {
|
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
|
// remove the connection from the list
|
||||||
if c, ok := s.connections.remove(connID); ok {
|
if c, ok := s.connections.remove(connID); ok {
|
||||||
if !c.disconnected {
|
if !c.disconnected {
|
||||||
c.disconnected = true
|
c.disconnected = true
|
||||||
// stop the ping timer
|
// stop the ping timer
|
||||||
c.pinger.Stop()
|
c.pinger.Stop()
|
||||||
// leave from all joined rooms
|
|
||||||
s.LeaveAll(connID)
|
|
||||||
// fire the disconnect callbacks, if any
|
// fire the disconnect callbacks, if any
|
||||||
c.fireDisconnect()
|
c.fireDisconnect()
|
||||||
// close the underline connection and return its error, if any.
|
// 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
|
// 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
|
package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
Loading…
Reference in New Issue
Block a user