mirror of
https://github.com/kataras/iris.git
synced 2025-12-18 02:17:05 +00:00
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
# Package information
|
||||
|
||||
This package is new and unique, if you notice a bug or issue [post it here](https://github.com/kataras/iris/issues).
|
||||
|
||||
# How to use
|
||||
|
||||
[E-Book section](https://kataras.gitbooks.io/iris/content/package-websocket.html)
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
On **OSX + Safari**, we had an issue which is **fixed** now. BUT by the browser's Engine Design the socket is not closed until the whole browser window is closed,
|
||||
so the **connection.OnDisconnect** event will fire when the user closes the **window browser**, **not just the browser's tab**.
|
||||
|
||||
- Relative issue: https://github.com/kataras/iris/issues/175
|
||||
@@ -1,258 +0,0 @@
|
||||
const stringMessageType = 0;
|
||||
const intMessageType = 1;
|
||||
const boolMessageType = 2;
|
||||
// bytes is missing here for reasons I will explain somewhen
|
||||
const jsonMessageType = 4;
|
||||
|
||||
const prefix = "iris-websocket-message:";
|
||||
const separator = ";";
|
||||
|
||||
const prefixLen = prefix.length;
|
||||
var separatorLen = separator.length;
|
||||
var prefixAndSepIdx = prefixLen + separatorLen - 1;
|
||||
var prefixIdx = prefixLen - 1;
|
||||
var separatorIdx = separatorLen - 1;
|
||||
|
||||
type onConnectFunc = () => void;
|
||||
type onDisconnectFunc = () => void;
|
||||
type onNativeMessageFunc = (websocketMessage: string) => void;
|
||||
type onMessageFunc = (message: any) => void;
|
||||
|
||||
class Ws {
|
||||
private conn: WebSocket;
|
||||
private isReady: boolean;
|
||||
|
||||
// events listeners
|
||||
|
||||
private connectListeners: onConnectFunc[] = [];
|
||||
private disconnectListeners: onDisconnectFunc[] = [];
|
||||
private nativeMessageListeners: onNativeMessageFunc[] = [];
|
||||
private messageListeners: { [event: string]: onMessageFunc[] } = {};
|
||||
|
||||
//
|
||||
|
||||
constructor(endpoint: string, protocols?: string[]) {
|
||||
if (!window["WebSocket"]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (endpoint.indexOf("ws") == -1) {
|
||||
endpoint = "ws://" + endpoint;
|
||||
}
|
||||
if (protocols != null && protocols.length > 0) {
|
||||
this.conn = new WebSocket(endpoint, protocols);
|
||||
} else {
|
||||
this.conn = new WebSocket(endpoint);
|
||||
}
|
||||
|
||||
this.conn.onopen = ((evt: Event): any => {
|
||||
this.fireConnect();
|
||||
this.isReady = true;
|
||||
return null;
|
||||
});
|
||||
|
||||
this.conn.onclose = ((evt: Event): any => {
|
||||
this.fireDisconnect();
|
||||
return null;
|
||||
});
|
||||
|
||||
this.conn.onmessage = ((evt: MessageEvent) => {
|
||||
this.messageReceivedFromConn(evt);
|
||||
});
|
||||
}
|
||||
|
||||
//utils
|
||||
|
||||
private isNumber(obj: any): boolean {
|
||||
return !isNaN(obj - 0) && obj !== null && obj !== "" && obj !== false;
|
||||
}
|
||||
|
||||
private isString(obj: any): boolean {
|
||||
return Object.prototype.toString.call(obj) == "[object String]";
|
||||
}
|
||||
|
||||
private isBoolean(obj: any): boolean {
|
||||
return typeof obj === 'boolean' ||
|
||||
(typeof obj === 'object' && typeof obj.valueOf() === 'boolean');
|
||||
}
|
||||
|
||||
private isJSON(obj: any): boolean {
|
||||
try {
|
||||
JSON.parse(obj);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// messages
|
||||
private _msg(event: string, messageType: number, dataMessage: string): string {
|
||||
|
||||
return prefix + event + separator + String(messageType) + separator + dataMessage;
|
||||
}
|
||||
|
||||
private encodeMessage(event: string, data: any): string {
|
||||
let m = "";
|
||||
let t = 0;
|
||||
if (this.isNumber(data)) {
|
||||
t = intMessageType;
|
||||
m = data.toString();
|
||||
} else if (this.isBoolean(data)) {
|
||||
t = boolMessageType;
|
||||
m = data.toString();
|
||||
} else if (this.isString(data)) {
|
||||
t = stringMessageType;
|
||||
m = data.toString();
|
||||
} else if (this.isJSON(data)) {
|
||||
//propably json-object
|
||||
t = jsonMessageType;
|
||||
m = JSON.stringify(data);
|
||||
} else {
|
||||
console.log("Invalid");
|
||||
}
|
||||
|
||||
return this._msg(event, t, m);
|
||||
}
|
||||
|
||||
private decodeMessage<T>(event: string, websocketMessage: string): T | any {
|
||||
//iris-websocket-message;user;4;themarshaledstringfromajsonstruct
|
||||
let skipLen = prefixLen + separatorLen + event.length + 2;
|
||||
if (websocketMessage.length < skipLen + 1) {
|
||||
return null;
|
||||
}
|
||||
let messageType = parseInt(websocketMessage.charAt(skipLen - 2));
|
||||
let theMessage = websocketMessage.substring(skipLen, websocketMessage.length);
|
||||
if (messageType == intMessageType) {
|
||||
return parseInt(theMessage);
|
||||
} else if (messageType == boolMessageType) {
|
||||
return Boolean(theMessage);
|
||||
} else if (messageType == stringMessageType) {
|
||||
return theMessage;
|
||||
} else if (messageType == jsonMessageType) {
|
||||
return JSON.parse(theMessage);
|
||||
} else {
|
||||
return null; // invalid
|
||||
}
|
||||
}
|
||||
|
||||
private getCustomEvent(websocketMessage: string): string {
|
||||
if (websocketMessage.length < prefixAndSepIdx) {
|
||||
return "";
|
||||
}
|
||||
let s = websocketMessage.substring(prefixAndSepIdx, websocketMessage.length);
|
||||
let evt = s.substring(0, s.indexOf(separator));
|
||||
|
||||
return evt;
|
||||
}
|
||||
|
||||
private getCustomMessage(event: string, websocketMessage: string): string {
|
||||
let eventIdx = websocketMessage.indexOf(event + separator);
|
||||
let s = websocketMessage.substring(eventIdx + event.length + separator.length+2, websocketMessage.length);
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Ws Events
|
||||
|
||||
// messageReceivedFromConn this is the func which decides
|
||||
// if it's a native websocket message or a custom iris-ws message
|
||||
// if native message then calls the fireNativeMessage
|
||||
// else calls the fireMessage
|
||||
//
|
||||
// remember Iris gives you the freedom of native websocket messages if you don't want to use this client side at all.
|
||||
private messageReceivedFromConn(evt: MessageEvent): void {
|
||||
//check if iris-ws message
|
||||
let message = <string>evt.data;
|
||||
if (message.indexOf(prefix) != -1) {
|
||||
let event = this.getCustomEvent(message);
|
||||
if (event != "") {
|
||||
// it's a custom message
|
||||
this.fireMessage(event, this.getCustomMessage(event, message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// it's a native websocket message
|
||||
this.fireNativeMessage(message);
|
||||
}
|
||||
|
||||
OnConnect(fn: onConnectFunc): void {
|
||||
if (this.isReady) {
|
||||
fn();
|
||||
}
|
||||
this.connectListeners.push(fn);
|
||||
}
|
||||
|
||||
fireConnect(): void {
|
||||
for (let i = 0; i < this.connectListeners.length; i++) {
|
||||
this.connectListeners[i]();
|
||||
}
|
||||
}
|
||||
|
||||
OnDisconnect(fn: onDisconnectFunc): void {
|
||||
this.disconnectListeners.push(fn);
|
||||
}
|
||||
|
||||
fireDisconnect(): void {
|
||||
for (let i = 0; i < this.disconnectListeners.length; i++) {
|
||||
this.disconnectListeners[i]();
|
||||
}
|
||||
}
|
||||
|
||||
OnMessage(cb: onNativeMessageFunc): void {
|
||||
this.nativeMessageListeners.push(cb);
|
||||
}
|
||||
|
||||
fireNativeMessage(websocketMessage: string): void {
|
||||
for (let i = 0; i < this.nativeMessageListeners.length; i++) {
|
||||
this.nativeMessageListeners[i](websocketMessage);
|
||||
}
|
||||
}
|
||||
|
||||
On(event: string, cb: onMessageFunc): void {
|
||||
if (this.messageListeners[event] == null || this.messageListeners[event] == undefined) {
|
||||
this.messageListeners[event] = [];
|
||||
}
|
||||
this.messageListeners[event].push(cb);
|
||||
}
|
||||
|
||||
fireMessage(event: string, message: any): void {
|
||||
for (let key in this.messageListeners) {
|
||||
if (this.messageListeners.hasOwnProperty(key)) {
|
||||
if (key == event) {
|
||||
for (let i = 0; i < this.messageListeners[key].length; i++) {
|
||||
this.messageListeners[key][i](message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
||||
// Ws Actions
|
||||
|
||||
Disconnect(): void {
|
||||
this.conn.close();
|
||||
}
|
||||
|
||||
// EmitMessage sends a native websocket message
|
||||
EmitMessage(websocketMessage: string): void {
|
||||
this.conn.send(websocketMessage);
|
||||
}
|
||||
|
||||
// Emit sends an iris-custom websocket message
|
||||
Emit(event: string, data: any): void {
|
||||
let messageStr = this.encodeMessage(event, data);
|
||||
this.EmitMessage(messageStr);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
// node-modules export {Ws};
|
||||
@@ -1,296 +0,0 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"bytes"
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/iris-contrib/websocket"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
type (
|
||||
// DisconnectFunc is the callback which fires when a client/connection closed
|
||||
DisconnectFunc func()
|
||||
// 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
|
||||
NativeMessageFunc func([]byte)
|
||||
// MessageFunc is the second argument to the Emitter's Emit functions.
|
||||
// A callback which should receives one parameter of type string, int, bool or any valid JSON/Go struct
|
||||
MessageFunc interface{}
|
||||
// Connection is the client
|
||||
Connection interface {
|
||||
// Emitter implements EmitMessage & Emit
|
||||
Emitter
|
||||
// ID returns the connection's identifier
|
||||
ID() string
|
||||
// OnDisconnect registers a callback which fires when this connection is closed by an error or manual
|
||||
OnDisconnect(DisconnectFunc)
|
||||
// OnError registers a callback which fires when this connection occurs an error
|
||||
OnError(ErrorFunc)
|
||||
// EmitError can be used to send a custom error message to the connection
|
||||
//
|
||||
// It does nothing more than firing the OnError listeners. It doesn't sends anything to the client.
|
||||
EmitError(errorMessage string)
|
||||
// To defines where server should send a message
|
||||
// returns an emmiter to send messages
|
||||
To(string) Emitter
|
||||
// OnMessage registers a callback which fires when native websocket message received
|
||||
OnMessage(NativeMessageFunc)
|
||||
// On registers a callback to a particular event which fires when a message to this event received
|
||||
On(string, MessageFunc)
|
||||
// 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)
|
||||
}
|
||||
|
||||
connection struct {
|
||||
underline *websocket.Conn
|
||||
id string
|
||||
send chan []byte
|
||||
onDisconnectListeners []DisconnectFunc
|
||||
onErrorListeners []ErrorFunc
|
||||
onNativeMessageListeners []NativeMessageFunc
|
||||
onEventListeners map[string][]MessageFunc
|
||||
// these were maden for performance only
|
||||
self Emitter // pre-defined emmiter than sends message to its self client
|
||||
broadcast Emitter // pre-defined emmiter that sends message to all except this
|
||||
all Emitter // pre-defined emmiter which sends message to all clients
|
||||
|
||||
server *server
|
||||
}
|
||||
)
|
||||
|
||||
var _ Connection = &connection{}
|
||||
|
||||
// connection implementation
|
||||
|
||||
func newConnection(websocketConn *websocket.Conn, s *server) *connection {
|
||||
c := &connection{
|
||||
id: utils.RandomString(64),
|
||||
underline: websocketConn,
|
||||
send: make(chan []byte, 256),
|
||||
onDisconnectListeners: make([]DisconnectFunc, 0),
|
||||
onErrorListeners: make([]ErrorFunc, 0),
|
||||
onNativeMessageListeners: make([]NativeMessageFunc, 0),
|
||||
onEventListeners: make(map[string][]MessageFunc, 0),
|
||||
server: s,
|
||||
}
|
||||
|
||||
c.self = newEmitter(c, c.id)
|
||||
c.broadcast = newEmitter(c, NotMe)
|
||||
c.all = newEmitter(c, All)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *connection) write(messageType int, data []byte) error {
|
||||
c.underline.SetWriteDeadline(time.Now().Add(c.server.config.WriteTimeout))
|
||||
return c.underline.WriteMessage(messageType, data)
|
||||
}
|
||||
|
||||
func (c *connection) writer() {
|
||||
ticker := time.NewTicker(c.server.config.PingPeriod)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
c.underline.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case msg, ok := <-c.send:
|
||||
if !ok {
|
||||
defer func() {
|
||||
|
||||
// FIX FOR: https://github.com/kataras/iris/issues/175
|
||||
// AS I TESTED ON TRIDENT ENGINE (INTERNET EXPLORER/SAFARI):
|
||||
// NAVIGATE TO SITE, CLOSE THE TAB, NOTHING HAPPENS
|
||||
// CLOSE THE WHOLE BROWSER, THEN THE c.conn is NOT NILL BUT ALL ITS FUNCTIONS PANICS, MEANS THAT IS THE STRUCT IS NOT NIL BUT THE WRITER/READER ARE NIL
|
||||
// THE ONLY SOLUTION IS TO RECOVER HERE AT ANY PANIC
|
||||
// THE FRAMETYPE = 8, c.closeSend = true
|
||||
// NOTE THAT THE CLIENT IS NOT DISCONNECTED UNTIL THE WHOLE WINDOW BROWSER CLOSED, this is engine's bug.
|
||||
//
|
||||
if err := recover(); err != nil {
|
||||
ticker.Stop()
|
||||
c.server.free <- c
|
||||
c.underline.Close()
|
||||
}
|
||||
}()
|
||||
c.write(websocket.CloseMessage, []byte{})
|
||||
return
|
||||
}
|
||||
|
||||
c.underline.SetWriteDeadline(time.Now().Add(c.server.config.WriteTimeout))
|
||||
res, err := c.underline.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res.Write(msg)
|
||||
|
||||
n := len(c.send)
|
||||
for i := 0; i < n; i++ {
|
||||
res.Write(<-c.send)
|
||||
}
|
||||
|
||||
if err := res.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// if err := c.write(websocket.TextMessage, msg); err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
case <-ticker.C:
|
||||
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connection) reader() {
|
||||
defer func() {
|
||||
c.server.free <- c
|
||||
c.underline.Close()
|
||||
}()
|
||||
conn := c.underline
|
||||
|
||||
conn.SetReadLimit(c.server.config.MaxMessageSize)
|
||||
conn.SetReadDeadline(time.Now().Add(c.server.config.PongTimeout))
|
||||
conn.SetPongHandler(func(s string) error {
|
||||
conn.SetReadDeadline(time.Now().Add(c.server.config.PongTimeout))
|
||||
return nil
|
||||
})
|
||||
|
||||
for {
|
||||
if _, data, err := conn.ReadMessage(); err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
c.EmitError(err.Error())
|
||||
}
|
||||
break
|
||||
} else {
|
||||
c.messageReceived(data)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// messageReceived checks the incoming message and fire the nativeMessage listeners or the event listeners (iris-ws custom message)
|
||||
func (c *connection) messageReceived(data []byte) {
|
||||
|
||||
if bytes.HasPrefix(data, prefixBytes) {
|
||||
customData := string(data)
|
||||
//it's a custom iris-ws message
|
||||
receivedEvt := getCustomEvent(customData)
|
||||
listeners := c.onEventListeners[receivedEvt]
|
||||
if listeners == nil { // if not listeners for this event exit from here
|
||||
return
|
||||
}
|
||||
customMessage, err := deserialize(receivedEvt, customData)
|
||||
if customMessage == nil || err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := range listeners {
|
||||
if fn, ok := listeners[i].(func()); ok { // its a simple func(){} callback
|
||||
fn()
|
||||
} else if fnString, ok := listeners[i].(func(string)); ok {
|
||||
|
||||
if msgString, is := customMessage.(string); is {
|
||||
fnString(msgString)
|
||||
} else if msgInt, is := customMessage.(int); is {
|
||||
// here if server side waiting for string but client side sent an int, just convert this int to a string
|
||||
fnString(strconv.Itoa(msgInt))
|
||||
}
|
||||
|
||||
} else if fnInt, ok := listeners[i].(func(int)); ok {
|
||||
fnInt(customMessage.(int))
|
||||
} else if fnBool, ok := listeners[i].(func(bool)); ok {
|
||||
fnBool(customMessage.(bool))
|
||||
} else if fnBytes, ok := listeners[i].(func([]byte)); ok {
|
||||
fnBytes(customMessage.([]byte))
|
||||
} else {
|
||||
listeners[i].(func(interface{}))(customMessage)
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// it's native websocket message
|
||||
for i := range c.onNativeMessageListeners {
|
||||
c.onNativeMessageListeners[i](data)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *connection) ID() string {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func (c *connection) fireDisconnect() {
|
||||
for i := range c.onDisconnectListeners {
|
||||
c.onDisconnectListeners[i]()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connection) OnDisconnect(cb DisconnectFunc) {
|
||||
c.onDisconnectListeners = append(c.onDisconnectListeners, cb)
|
||||
}
|
||||
|
||||
func (c *connection) OnError(cb ErrorFunc) {
|
||||
c.onErrorListeners = append(c.onErrorListeners, cb)
|
||||
}
|
||||
|
||||
func (c *connection) EmitError(errorMessage string) {
|
||||
for _, cb := range c.onErrorListeners {
|
||||
cb(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connection) To(to string) Emitter {
|
||||
if to == NotMe { // if send to all except me, then return the pre-defined emmiter, and so on
|
||||
return c.broadcast
|
||||
} else if to == All {
|
||||
return c.all
|
||||
} else if to == c.id {
|
||||
return c.self
|
||||
}
|
||||
// is an emmiter to another client/connection
|
||||
return newEmitter(c, to)
|
||||
}
|
||||
|
||||
func (c *connection) EmitMessage(nativeMessage []byte) error {
|
||||
return c.self.EmitMessage(nativeMessage)
|
||||
}
|
||||
|
||||
func (c *connection) Emit(event string, message interface{}) error {
|
||||
return c.self.Emit(event, message)
|
||||
}
|
||||
|
||||
func (c *connection) OnMessage(cb NativeMessageFunc) {
|
||||
c.onNativeMessageListeners = append(c.onNativeMessageListeners, cb)
|
||||
}
|
||||
|
||||
func (c *connection) On(event string, cb MessageFunc) {
|
||||
if c.onEventListeners[event] == nil {
|
||||
c.onEventListeners[event] = make([]MessageFunc, 0)
|
||||
}
|
||||
|
||||
c.onEventListeners[event] = append(c.onEventListeners[event], cb)
|
||||
}
|
||||
|
||||
func (c *connection) Join(roomName string) {
|
||||
payload := roomPayload{roomName, c.id}
|
||||
c.server.join <- payload
|
||||
}
|
||||
|
||||
func (c *connection) Leave(roomName string) {
|
||||
payload := roomPayload{roomName, c.id}
|
||||
c.server.leave <- payload
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1,50 +0,0 @@
|
||||
package websocket
|
||||
|
||||
const (
|
||||
// All is the string which the Emitter use to send a message to all
|
||||
All = ""
|
||||
// NotMe is the string which the Emitter use to send a message to all except this connection
|
||||
NotMe = ";iris;to;all;except;me;"
|
||||
// Broadcast is the string which the Emitter use to send a message to all except this connection, same as 'NotMe'
|
||||
Broadcast = NotMe
|
||||
)
|
||||
|
||||
type (
|
||||
// Emitter is the message/or/event manager
|
||||
Emitter interface {
|
||||
// EmitMessage sends a native websocket message
|
||||
EmitMessage([]byte) error
|
||||
// Emit sends a message on a particular event
|
||||
Emit(string, interface{}) error
|
||||
}
|
||||
|
||||
emitter struct {
|
||||
conn *connection
|
||||
to string
|
||||
}
|
||||
)
|
||||
|
||||
var _ Emitter = &emitter{}
|
||||
|
||||
// emitter implementation
|
||||
|
||||
func newEmitter(c *connection, to string) *emitter {
|
||||
return &emitter{conn: c, to: to}
|
||||
}
|
||||
|
||||
func (e *emitter) EmitMessage(nativeMessage []byte) error {
|
||||
mp := messagePayload{e.conn.id, e.to, nativeMessage}
|
||||
e.conn.server.messages <- mp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *emitter) Emit(event string, data interface{}) error {
|
||||
message, err := serialize(event, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.EmitMessage([]byte(message))
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1,145 +0,0 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
/*
|
||||
serializer, [de]serialize the messages from the client to the server and from the server to the client
|
||||
*/
|
||||
|
||||
// The same values are exists on client side also
|
||||
const (
|
||||
stringMessageType messageType = iota
|
||||
intMessageType
|
||||
boolMessageType
|
||||
bytesMessageType
|
||||
jsonMessageType
|
||||
)
|
||||
|
||||
const (
|
||||
prefix = "iris-websocket-message:"
|
||||
separator = ";"
|
||||
prefixLen = len(prefix)
|
||||
separatorLen = len(separator)
|
||||
prefixAndSepIdx = prefixLen + separatorLen - 1
|
||||
prefixIdx = prefixLen - 1
|
||||
separatorIdx = separatorLen - 1
|
||||
)
|
||||
|
||||
var (
|
||||
separatorByte = separator[0]
|
||||
buf = utils.NewBufferPool(256)
|
||||
prefixBytes = []byte(prefix)
|
||||
)
|
||||
|
||||
type (
|
||||
messageType uint8
|
||||
)
|
||||
|
||||
func (m messageType) String() string {
|
||||
return strconv.Itoa(int(m))
|
||||
}
|
||||
|
||||
func (m messageType) Name() string {
|
||||
if m == stringMessageType {
|
||||
return "string"
|
||||
} else if m == intMessageType {
|
||||
return "int"
|
||||
} else if m == boolMessageType {
|
||||
return "bool"
|
||||
} else if m == bytesMessageType {
|
||||
return "[]byte"
|
||||
} else if m == jsonMessageType {
|
||||
return "json"
|
||||
}
|
||||
|
||||
return "Invalid(" + m.String() + ")"
|
||||
|
||||
}
|
||||
|
||||
// serialize serializes a custom websocket message from server to be delivered to the client
|
||||
// returns the string form of the message
|
||||
// Supported data types are: string, int, bool, bytes and JSON.
|
||||
func serialize(event string, data interface{}) (string, error) {
|
||||
var msgType messageType
|
||||
var dataMessage string
|
||||
|
||||
if s, ok := data.(string); ok {
|
||||
msgType = stringMessageType
|
||||
dataMessage = s
|
||||
} else if i, ok := data.(int); ok {
|
||||
msgType = intMessageType
|
||||
dataMessage = strconv.Itoa(i)
|
||||
} else if b, ok := data.(bool); ok {
|
||||
msgType = boolMessageType
|
||||
dataMessage = strconv.FormatBool(b)
|
||||
} else if by, ok := data.([]byte); ok {
|
||||
msgType = bytesMessageType
|
||||
dataMessage = string(by)
|
||||
} else {
|
||||
//we suppose is json
|
||||
res, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
msgType = jsonMessageType
|
||||
dataMessage = string(res)
|
||||
}
|
||||
|
||||
b := buf.Get()
|
||||
b.WriteString(prefix)
|
||||
b.WriteString(event)
|
||||
b.WriteString(separator)
|
||||
b.WriteString(msgType.String())
|
||||
b.WriteString(separator)
|
||||
b.WriteString(dataMessage)
|
||||
dataMessage = b.String()
|
||||
buf.Put(b)
|
||||
|
||||
return dataMessage, nil
|
||||
|
||||
}
|
||||
|
||||
// deserialize deserializes a custom websocket message from the client
|
||||
// ex: iris-websocket-message;chat;4;themarshaledstringfromajsonstruct will return 'hello' as string
|
||||
// Supported data types are: string, int, bool, bytes and JSON.
|
||||
func deserialize(event string, websocketMessage string) (message interface{}, err error) {
|
||||
t, formaterr := strconv.Atoi(websocketMessage[prefixAndSepIdx+len(event)+1 : prefixAndSepIdx+len(event)+2]) // in order to iris-websocket-message;user;-> 4
|
||||
if formaterr != nil {
|
||||
return nil, formaterr
|
||||
}
|
||||
_type := messageType(t)
|
||||
_message := websocketMessage[prefixAndSepIdx+len(event)+3:] // in order to iris-websocket-message;user;4; -> themarshaledstringfromajsonstruct
|
||||
|
||||
if _type == stringMessageType {
|
||||
message = string(_message)
|
||||
} else if _type == intMessageType {
|
||||
message, err = strconv.Atoi(_message)
|
||||
} else if _type == boolMessageType {
|
||||
message, err = strconv.ParseBool(_message)
|
||||
} else if _type == bytesMessageType {
|
||||
message = []byte(_message)
|
||||
} else if _type == jsonMessageType {
|
||||
err = json.Unmarshal([]byte(_message), message)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Type %s is invalid for message: %s", _type.Name(), websocketMessage)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getCustomEvent return empty string when the websocketMessage is native message
|
||||
func getCustomEvent(websocketMessage string) string {
|
||||
if len(websocketMessage) < prefixAndSepIdx {
|
||||
return ""
|
||||
}
|
||||
s := websocketMessage[prefixAndSepIdx:]
|
||||
evt := s[:strings.IndexByte(s, separatorByte)]
|
||||
return evt
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/iris-contrib/websocket"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
type (
|
||||
// ConnectionFunc is the callback which fires when a client/connection is connected to the server.
|
||||
// Receives one parameter which is the Connection
|
||||
ConnectionFunc func(Connection)
|
||||
// Rooms is just a map with key a string and value slice of string
|
||||
Rooms map[string][]string
|
||||
// Server is the websocket server
|
||||
Server interface {
|
||||
// Upgrade upgrades the client in order websocket works
|
||||
Upgrade(context.IContext) error
|
||||
// OnConnection registers a callback which fires when a connection/client is connected to the server
|
||||
OnConnection(ConnectionFunc)
|
||||
// Config returns a pointer to server's configs
|
||||
Config() *config.Websocket
|
||||
}
|
||||
|
||||
// roomPayload is used as payload from the connection to the server
|
||||
roomPayload struct {
|
||||
roomName string
|
||||
connectionID string
|
||||
}
|
||||
|
||||
// payloads, connection -> server
|
||||
messagePayload struct {
|
||||
from string
|
||||
to string
|
||||
data []byte
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
server struct {
|
||||
config *config.Websocket
|
||||
upgrader websocket.Upgrader
|
||||
put chan *connection
|
||||
free chan *connection
|
||||
connections map[string]*connection
|
||||
join chan roomPayload
|
||||
leave chan roomPayload
|
||||
rooms Rooms // by default a connection is joined to a room which has the connection id as its name
|
||||
mu sync.Mutex // for rooms
|
||||
messages chan messagePayload
|
||||
onConnectionListeners []ConnectionFunc
|
||||
//connectionPool *sync.Pool // sadly I can't make this because the websocket connection is live until is closed.
|
||||
}
|
||||
)
|
||||
|
||||
var _ Server = &server{}
|
||||
|
||||
// server implementation
|
||||
|
||||
// newServer creates a websocket server and returns it
|
||||
func newServer(c *config.Websocket) *server {
|
||||
s := &server{
|
||||
config: c,
|
||||
put: make(chan *connection),
|
||||
free: make(chan *connection),
|
||||
connections: make(map[string]*connection),
|
||||
join: make(chan roomPayload, 1), // buffered because join can be called immediately on connection connected
|
||||
leave: make(chan roomPayload),
|
||||
rooms: make(Rooms),
|
||||
messages: make(chan messagePayload, 1), // buffered because messages can be sent/received immediately on connection connected
|
||||
onConnectionListeners: make([]ConnectionFunc, 0),
|
||||
}
|
||||
|
||||
s.upgrader = websocket.Custom(s.handleConnection, s.config.ReadBufferSize, s.config.WriteBufferSize, false)
|
||||
go s.serve() // start the server automatically
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *server) Config() *config.Websocket {
|
||||
return s.config
|
||||
}
|
||||
|
||||
func (s *server) Upgrade(ctx context.IContext) error {
|
||||
return s.upgrader.Upgrade(ctx)
|
||||
}
|
||||
|
||||
func (s *server) handleConnection(websocketConn *websocket.Conn) {
|
||||
c := newConnection(websocketConn, s)
|
||||
s.put <- c
|
||||
go c.writer()
|
||||
c.reader()
|
||||
}
|
||||
|
||||
func (s *server) OnConnection(cb ConnectionFunc) {
|
||||
s.onConnectionListeners = append(s.onConnectionListeners, cb)
|
||||
}
|
||||
|
||||
func (s *server) joinRoom(roomName string, connID string) {
|
||||
s.mu.Lock()
|
||||
if s.rooms[roomName] == nil {
|
||||
s.rooms[roomName] = make([]string, 0)
|
||||
}
|
||||
s.rooms[roomName] = append(s.rooms[roomName], connID)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *server) leaveRoom(roomName string, connID string) {
|
||||
s.mu.Lock()
|
||||
if s.rooms[roomName] != nil {
|
||||
for i := range s.rooms[roomName] {
|
||||
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]
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(s.rooms[roomName]) == 0 { // if room is empty then delete it
|
||||
delete(s.rooms, roomName)
|
||||
}
|
||||
}
|
||||
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *server) serve() {
|
||||
for {
|
||||
select {
|
||||
case c := <-s.put: // connection connected
|
||||
s.connections[c.id] = c
|
||||
// make and join a room with the connection's id
|
||||
s.rooms[c.id] = make([]string, 0)
|
||||
s.rooms[c.id] = []string{c.id}
|
||||
for i := range s.onConnectionListeners {
|
||||
s.onConnectionListeners[i](c)
|
||||
}
|
||||
case c := <-s.free: // connection closed
|
||||
if _, found := s.connections[c.id]; found {
|
||||
// leave from all rooms
|
||||
for roomName := range s.rooms {
|
||||
s.leaveRoom(roomName, c.id)
|
||||
}
|
||||
delete(s.connections, c.id)
|
||||
close(c.send)
|
||||
c.fireDisconnect()
|
||||
|
||||
}
|
||||
case join := <-s.join:
|
||||
s.joinRoom(join.roomName, join.connectionID)
|
||||
case leave := <-s.leave:
|
||||
s.leaveRoom(leave.roomName, leave.connectionID)
|
||||
case msg := <-s.messages: // message received from the connection
|
||||
if msg.to != All && msg.to != NotMe && s.rooms[msg.to] != nil {
|
||||
// it suppose to send the message to a room
|
||||
for _, connectionIDInsideRoom := range s.rooms[msg.to] {
|
||||
if c, connected := s.connections[connectionIDInsideRoom]; connected {
|
||||
c.send <- msg.data //here we send it without need to continue below
|
||||
} else {
|
||||
// the connection is not connected but it's inside the room, we remove it on disconnect but for ANY CASE:
|
||||
s.leaveRoom(c.id, msg.to)
|
||||
}
|
||||
}
|
||||
|
||||
} else { // it suppose to send the message to all opened connections or to all except the sender
|
||||
for connID, c := range s.connections {
|
||||
if msg.to != All { // if it's not suppose to send to all connections (including itself)
|
||||
if msg.to == NotMe && msg.from == connID { // if broadcast to other connections except this
|
||||
continue //here we do the opossite of previous block, just skip this connection when it's suppose to send the message to all connections except the sender
|
||||
}
|
||||
}
|
||||
select {
|
||||
case s.connections[connID].send <- msg.data: //send the message back to the connection in order to send it to the client
|
||||
default:
|
||||
close(c.send)
|
||||
delete(s.connections, connID)
|
||||
c.fireDisconnect()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1,288 +0,0 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"github.com/iris-contrib/logger"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// to avoid the import cycle to /kataras/iris. The ws package is used inside iris' station configuration
|
||||
// inside Iris' configuration like kataras/iris/sessions, kataras/iris/render/rest, kataras/iris/render/template, kataras/iris/server and so on.
|
||||
type irisStation interface {
|
||||
H_(string, string, func(context.IContext)) func(string)
|
||||
StaticContent(string, string, []byte) func(string)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// New returns a new running websocket server, registers this to the iris station
|
||||
//
|
||||
// Note that:
|
||||
// This is not usable for you, unless you need more than one websocket server,
|
||||
// because iris' station already has one which you can configure and start
|
||||
//
|
||||
// This is deprecated after rc-1, now we create the server and after register it
|
||||
// because I want to be able to call the Websocket via a property and no via func before iris.Listen.
|
||||
func New(station irisStation, c *config.Websocket, logger *logger.Logger) Server {
|
||||
if c.Endpoint == "" {
|
||||
//station.Logger().Panicf("Websockets - config's Endpoint is empty, you have to set it in order to enable and start the websocket server!!. Refer to the docs if you can't figure out.")
|
||||
return nil
|
||||
}
|
||||
server := newServer(c)
|
||||
RegisterServer(station, server, logger)
|
||||
return server
|
||||
}
|
||||
|
||||
// NewServer creates a websocket server and returns it
|
||||
func NewServer(c *config.Websocket) Server {
|
||||
return newServer(c)
|
||||
}
|
||||
|
||||
// RegisterServer registers the handlers for the websocket server
|
||||
// it's a bridge between station and websocket server
|
||||
func RegisterServer(station irisStation, server Server, logger *logger.Logger) {
|
||||
c := server.Config()
|
||||
if c.Endpoint == "" {
|
||||
return
|
||||
}
|
||||
|
||||
websocketHandler := func(ctx context.IContext) {
|
||||
if err := server.Upgrade(ctx); err != nil {
|
||||
logger.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Headers != nil && len(c.Headers) > 0 { // only for performance matter just re-create the websocketHandler if we have headers to set
|
||||
websocketHandler = func(ctx context.IContext) {
|
||||
for k, v := range c.Headers {
|
||||
ctx.SetHeader(k, v)
|
||||
}
|
||||
|
||||
if err := server.Upgrade(ctx); err != nil {
|
||||
logger.Panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
station.H_("GET", c.Endpoint, websocketHandler)
|
||||
// serve the client side on domain:port/iris-ws.js
|
||||
station.StaticContent("/iris-ws.js", "application/json", clientSource)
|
||||
|
||||
}
|
||||
|
||||
var clientSource = []byte(`var stringMessageType = 0;
|
||||
var intMessageType = 1;
|
||||
var boolMessageType = 2;
|
||||
// bytes is missing here for reasons I will explain somewhen
|
||||
var jsonMessageType = 4;
|
||||
var prefix = "iris-websocket-message:";
|
||||
var separator = ";";
|
||||
var prefixLen = prefix.length;
|
||||
var separatorLen = separator.length;
|
||||
var prefixAndSepIdx = prefixLen + separatorLen - 1;
|
||||
var prefixIdx = prefixLen - 1;
|
||||
var separatorIdx = separatorLen - 1;
|
||||
var Ws = (function () {
|
||||
//
|
||||
function Ws(endpoint, protocols) {
|
||||
var _this = this;
|
||||
// events listeners
|
||||
this.connectListeners = [];
|
||||
this.disconnectListeners = [];
|
||||
this.nativeMessageListeners = [];
|
||||
this.messageListeners = {};
|
||||
if (!window["WebSocket"]) {
|
||||
return;
|
||||
}
|
||||
if (endpoint.indexOf("ws") == -1) {
|
||||
endpoint = "ws://" + endpoint;
|
||||
}
|
||||
if (protocols != null && protocols.length > 0) {
|
||||
this.conn = new WebSocket(endpoint, protocols);
|
||||
}
|
||||
else {
|
||||
this.conn = new WebSocket(endpoint);
|
||||
}
|
||||
this.conn.onopen = (function (evt) {
|
||||
_this.fireConnect();
|
||||
_this.isReady = true;
|
||||
return null;
|
||||
});
|
||||
this.conn.onclose = (function (evt) {
|
||||
_this.fireDisconnect();
|
||||
return null;
|
||||
});
|
||||
this.conn.onmessage = (function (evt) {
|
||||
_this.messageReceivedFromConn(evt);
|
||||
});
|
||||
}
|
||||
//utils
|
||||
Ws.prototype.isNumber = function (obj) {
|
||||
return !isNaN(obj - 0) && obj !== null && obj !== "" && obj !== false;
|
||||
};
|
||||
Ws.prototype.isString = function (obj) {
|
||||
return Object.prototype.toString.call(obj) == "[object String]";
|
||||
};
|
||||
Ws.prototype.isBoolean = function (obj) {
|
||||
return typeof obj === 'boolean' ||
|
||||
(typeof obj === 'object' && typeof obj.valueOf() === 'boolean');
|
||||
};
|
||||
Ws.prototype.isJSON = function (obj) {
|
||||
try {
|
||||
JSON.parse(obj);
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
//
|
||||
// messages
|
||||
Ws.prototype._msg = function (event, messageType, dataMessage) {
|
||||
return prefix + event + separator + String(messageType) + separator + dataMessage;
|
||||
};
|
||||
Ws.prototype.encodeMessage = function (event, data) {
|
||||
var m = "";
|
||||
var t = 0;
|
||||
if (this.isNumber(data)) {
|
||||
t = intMessageType;
|
||||
m = data.toString();
|
||||
}
|
||||
else if (this.isBoolean(data)) {
|
||||
t = boolMessageType;
|
||||
m = data.toString();
|
||||
}
|
||||
else if (this.isString(data)) {
|
||||
t = stringMessageType;
|
||||
m = data.toString();
|
||||
}
|
||||
else if (this.isJSON(data)) {
|
||||
//propably json-object
|
||||
t = jsonMessageType;
|
||||
m = JSON.stringify(data);
|
||||
}
|
||||
else {
|
||||
console.log("Invalid");
|
||||
}
|
||||
return this._msg(event, t, m);
|
||||
};
|
||||
Ws.prototype.decodeMessage = function (event, websocketMessage) {
|
||||
//iris-websocket-message;user;4;themarshaledstringfromajsonstruct
|
||||
var skipLen = prefixLen + separatorLen + event.length + 2;
|
||||
if (websocketMessage.length < skipLen + 1) {
|
||||
return null;
|
||||
}
|
||||
var messageType = parseInt(websocketMessage.charAt(skipLen - 2));
|
||||
var theMessage = websocketMessage.substring(skipLen, websocketMessage.length);
|
||||
if (messageType == intMessageType) {
|
||||
return parseInt(theMessage);
|
||||
}
|
||||
else if (messageType == boolMessageType) {
|
||||
return Boolean(theMessage);
|
||||
}
|
||||
else if (messageType == stringMessageType) {
|
||||
return theMessage;
|
||||
}
|
||||
else if (messageType == jsonMessageType) {
|
||||
return JSON.parse(theMessage);
|
||||
}
|
||||
else {
|
||||
return null; // invalid
|
||||
}
|
||||
};
|
||||
Ws.prototype.getCustomEvent = function (websocketMessage) {
|
||||
if (websocketMessage.length < prefixAndSepIdx) {
|
||||
return "";
|
||||
}
|
||||
var s = websocketMessage.substring(prefixAndSepIdx, websocketMessage.length);
|
||||
var evt = s.substring(0, s.indexOf(separator));
|
||||
return evt;
|
||||
};
|
||||
Ws.prototype.getCustomMessage = function (event, websocketMessage) {
|
||||
var eventIdx = websocketMessage.indexOf(event + separator);
|
||||
var s = websocketMessage.substring(eventIdx + event.length + separator.length + 2, websocketMessage.length);
|
||||
return s;
|
||||
};
|
||||
//
|
||||
// Ws Events
|
||||
// messageReceivedFromConn this is the func which decides
|
||||
// if it's a native websocket message or a custom iris-ws message
|
||||
// if native message then calls the fireNativeMessage
|
||||
// else calls the fireMessage
|
||||
//
|
||||
// remember Iris gives you the freedom of native websocket messages if you don't want to use this client side at all.
|
||||
Ws.prototype.messageReceivedFromConn = function (evt) {
|
||||
//check if iris-ws message
|
||||
var message = evt.data;
|
||||
if (message.indexOf(prefix) != -1) {
|
||||
var event_1 = this.getCustomEvent(message);
|
||||
if (event_1 != "") {
|
||||
// it's a custom message
|
||||
this.fireMessage(event_1, this.getCustomMessage(event_1, message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// it's a native websocket message
|
||||
this.fireNativeMessage(message);
|
||||
};
|
||||
Ws.prototype.OnConnect = function (fn) {
|
||||
if (this.isReady) {
|
||||
fn();
|
||||
}
|
||||
this.connectListeners.push(fn);
|
||||
};
|
||||
Ws.prototype.fireConnect = function () {
|
||||
for (var i = 0; i < this.connectListeners.length; i++) {
|
||||
this.connectListeners[i]();
|
||||
}
|
||||
};
|
||||
Ws.prototype.OnDisconnect = function (fn) {
|
||||
this.disconnectListeners.push(fn);
|
||||
};
|
||||
Ws.prototype.fireDisconnect = function () {
|
||||
for (var i = 0; i < this.disconnectListeners.length; i++) {
|
||||
this.disconnectListeners[i]();
|
||||
}
|
||||
};
|
||||
Ws.prototype.OnMessage = function (cb) {
|
||||
this.nativeMessageListeners.push(cb);
|
||||
};
|
||||
Ws.prototype.fireNativeMessage = function (websocketMessage) {
|
||||
for (var i = 0; i < this.nativeMessageListeners.length; i++) {
|
||||
this.nativeMessageListeners[i](websocketMessage);
|
||||
}
|
||||
};
|
||||
Ws.prototype.On = function (event, cb) {
|
||||
if (this.messageListeners[event] == null || this.messageListeners[event] == undefined) {
|
||||
this.messageListeners[event] = [];
|
||||
}
|
||||
this.messageListeners[event].push(cb);
|
||||
};
|
||||
Ws.prototype.fireMessage = function (event, message) {
|
||||
for (var key in this.messageListeners) {
|
||||
if (this.messageListeners.hasOwnProperty(key)) {
|
||||
if (key == event) {
|
||||
for (var i = 0; i < this.messageListeners[key].length; i++) {
|
||||
this.messageListeners[key][i](message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
//
|
||||
// Ws Actions
|
||||
Ws.prototype.Disconnect = function () {
|
||||
this.conn.close();
|
||||
};
|
||||
// EmitMessage sends a native websocket message
|
||||
Ws.prototype.EmitMessage = function (websocketMessage) {
|
||||
this.conn.send(websocketMessage);
|
||||
};
|
||||
// Emit sends an iris-custom websocket message
|
||||
Ws.prototype.Emit = function (event, data) {
|
||||
var messageStr = this.encodeMessage(event, data);
|
||||
this.EmitMessage(messageStr);
|
||||
};
|
||||
return Ws;
|
||||
}());
|
||||
`)
|
||||
Reference in New Issue
Block a user