Update to version 11.0.1. Feature request implemented: https://github.com/kataras/iris/issues/1113

Former-commit-id: 0ce38dbacc2458fe327fa4401fdde1e69c8aacb0
This commit is contained in:
Gerasimos (Makis) Maropoulos 2018-10-28 01:19:22 +03:00
parent 8950ae7bb9
commit be32418bc1
16 changed files with 272 additions and 159 deletions

View File

@ -17,6 +17,32 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you. **How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you.
# Su, 28 October 2018 | v11.0.1
- Update benchmarks: https://github.com/kataras/iris/commit/d1b47b1ec65ae77a2ca7485e510386f4a5456ac4
- Add link for third-party source benchmarks: https://github.com/kataras/iris/commit/64e80a7ee5c23ed938ddc8b68d181a25420c7653
- Add optionally custom low-level websocket message data prefix as requested at: https://github.com/kataras/iris/issues/1113 by [@jjhesk](https://github.com/jjhesk). Example:
```go
app := iris.New()
// [...]
wsServer := websocket.New(websocket.Config{
// [...]
EvtMessagePrefix: []byte("my-custom-prefix:"),
})
// [...]
// serve the javascript built'n client-side library,
// see websockets.html script tags, this path is used.
app.Any("/iris-ws.js", func(ctx iris.Context) {
ctx.Write(wsServer.ClientSource)
})
// [...]
```
# Su, 21 October 2018 | v11.0.0 # Su, 21 October 2018 | v11.0.0
For the craziest of us, click [here](https://github.com/kataras/iris/compare/v10.7.0...v11) 🔥 to find out the commits and the code changes since our previous release. For the craziest of us, click [here](https://github.com/kataras/iris/compare/v10.7.0...v11) 🔥 to find out the commits and the code changes since our previous release.

View File

@ -17,9 +17,13 @@
**Πώς να αναβαθμίσετε**: Ανοίξτε την γραμμή εντολών σας και εκτελέστε αυτήν την εντολή: `go get -u github.com/kataras/iris` ή αφήστε το αυτόματο updater να το κάνει αυτό για σας. **Πώς να αναβαθμίσετε**: Ανοίξτε την γραμμή εντολών σας και εκτελέστε αυτήν την εντολή: `go get -u github.com/kataras/iris` ή αφήστε το αυτόματο updater να το κάνει αυτό για σας.
# Su, 28 October 2018 | v11.0.1
Πατήστε [εδώ](https://github.com/kataras/iris/blob/master/HISTORY.md#su-28-october-2018--v1101) για να διαβάσετε στα αγγλικά τις αλλαγές και το νέο feature που φέρνει το τελευταίο patch για την έκδοση 11.
# Su, 21 October 2018 | v11.0.0 # Su, 21 October 2018 | v11.0.0
Πατήστε [εδώ](https://github.com/kataras/iris/blob/master/HISTORY.md#su-21-october-2018--v11) για να διαβάσετε στα αγγλικά τις αλλαγές και τα νέα features που φέρνει νεότερη έκδοση του Iris, version 11. Πατήστε [εδώ](https://github.com/kataras/iris/blob/master/HISTORY.md#su-21-october-2018--v1100) για να διαβάσετε στα αγγλικά τις αλλαγές και τα νέα features που φέρνει νεότερη έκδοση του Iris, version 11.
# Sat, 11 August 2018 | v10.7.0 # Sat, 11 August 2018 | v10.7.0

View File

@ -17,9 +17,13 @@ Developers tidak diwajibkan untuk melakukan upgrade apabila mereka tidak membutu
**Cara Upgrade**: Bukan command-line anda dan eksekuis perintah ini: `go get -u github.com/kataras/iris` atau biarkan updater otomatis melakukannya untuk anda. **Cara Upgrade**: Bukan command-line anda dan eksekuis perintah ini: `go get -u github.com/kataras/iris` atau biarkan updater otomatis melakukannya untuk anda.
# Su, 28 October 2018 | v11.0.1
This history entry is not translated yet to the Indonesian language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-28-october-2018--v1101) instead.
# Su, 21 October 2018 | v11.0.0 # Su, 21 October 2018 | v11.0.0
This history entry is not translated yet to the Indonesian language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-21-october-2018--v11) instead. This history entry is not translated yet to the Indonesian language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-21-october-2018--v1100) instead.
# Sat, 11 August 2018 | v10.7.0 # Sat, 11 August 2018 | v10.7.0

View File

@ -17,9 +17,13 @@
**如何升级**: 打开命令行执行以下命令: `go get -u github.com/kataras/iris` 或者等待自动更新。 **如何升级**: 打开命令行执行以下命令: `go get -u github.com/kataras/iris` 或者等待自动更新。
# Su, 28 October 2018 | v11.0.1
This history entry is not translated yet to the Chinese language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-28-october-2018--v1101) instead.
# Su, 21 October 2018 | v11.0.0 # Su, 21 October 2018 | v11.0.0
This history entry is not translated yet to the Chinese language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-21-october-2018--v11) instead. This history entry is not translated yet to the Chinese language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-21-october-2018--v1100) instead.
# Sat, 11 August 2018 | v10.7.0 # Sat, 11 August 2018 | v10.7.0

View File

@ -1011,7 +1011,7 @@ Iris, unlike others, is 100% compatible with the standards and that's why the ma
## Support ## Support
- [HISTORY](HISTORY.md#su-21-october-2018--v1100) file is your best friend, it contains information about the latest features and changes - [HISTORY](HISTORY.md#su-28-october-2018--v1101) file is your best friend, it contains information about the latest features and changes
- Did you happen to find a bug? Post it at [github issues](https://github.com/kataras/iris/issues) - Did you happen to find a bug? Post it at [github issues](https://github.com/kataras/iris/issues)
- Do you have any questions or need to speak with someone experienced to solve a problem at real-time? Join us to the [community chat](https://chat.iris-go.com) - Do you have any questions or need to speak with someone experienced to solve a problem at real-time? Join us to the [community chat](https://chat.iris-go.com)
- Complete our form-based user experience report by clicking [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) - Complete our form-based user experience report by clicking [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -1 +1 @@
11.0.0:https://github.com/kataras/iris/blob/master/HISTORY.md#su-21-october-2018--v1100 11.0.1:https://github.com/kataras/iris/blob/master/HISTORY.md#su-28-october-2018--v1101

View File

@ -26,8 +26,13 @@ func main() {
func setupWebsocket(app *iris.Application) { func setupWebsocket(app *iris.Application) {
// create our echo websocket server // create our echo websocket server
ws := websocket.New(websocket.Config{ ws := websocket.New(websocket.Config{
// These are low-level optionally fields,
// user/client can't see those values.
ReadBufferSize: 1024, ReadBufferSize: 1024,
WriteBufferSize: 1024, WriteBufferSize: 1024,
// only javascript client-side code has the same rule,
// which you serve using the ws.ClientSource (see below).
EvtMessagePrefix: []byte("my-custom-prefix:"),
}) })
ws.OnConnection(handleConnection) ws.OnConnection(handleConnection)
@ -38,7 +43,7 @@ func setupWebsocket(app *iris.Application) {
// serve the javascript built'n client-side library, // serve the javascript built'n client-side library,
// see websockets.html script tags, this path is used. // see websockets.html script tags, this path is used.
app.Any("/iris-ws.js", func(ctx iris.Context) { app.Any("/iris-ws.js", func(ctx iris.Context) {
ctx.Write(websocket.ClientSource) ctx.Write(ws.ClientSource)
}) })
} }

View File

@ -89,7 +89,7 @@ func SendMessage(serverID, to, method, message string) error {
func SendtBytes(serverID, to, method string, message []byte) error { func SendtBytes(serverID, to, method string, message []byte) error {
// look https://github.com/kataras/iris/blob/master/websocket/message.go , client.go and client.js // look https://github.com/kataras/iris/blob/master/websocket/message.go , client.go and client.js
// to understand the buffer line: // to understand the buffer line:
buffer := []byte(fmt.Sprintf("iris-websocket-message:%v;0;%v;%v;", method, serverID, to)) buffer := []byte(fmt.Sprintf("%s%v;0;%v;%v;", websocket.DefaultEvtMessageKey, method, serverID, to))
buffer = append(buffer, message...) buffer = append(buffer, message...)
_, err := WS.Write(buffer) _, err := WS.Write(buffer)
if err != nil { if err != nil {

2
doc.go
View File

@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
Current Version Current Version
11.0.0 11.0.1
Installation Installation

View File

@ -33,7 +33,7 @@ import (
var ( var (
// Version is the current version number of the Iris Web Framework. // Version is the current version number of the Iris Web Framework.
Version = "11.0.0" Version = "11.0.1"
) )
// HTTP status codes as registered with IANA. // HTTP status codes as registered with IANA.

View File

@ -7,7 +7,7 @@ import (
) )
// ClientHandler is the handler which serves the javascript client-side // ClientHandler is the handler which serves the javascript client-side
// library. It uses a small cache based on the iris/context.StaticCacheDuration. // library. It uses a small cache based on the iris/context.WriteWithExpiration.
func ClientHandler() context.Handler { func ClientHandler() context.Handler {
modNow := time.Now() modNow := time.Now()
return func(ctx context.Context) { return func(ctx context.Context) {
@ -20,12 +20,12 @@ func ClientHandler() context.Handler {
} }
} }
// ClientSource the client-side javascript raw source code // ClientSource the client-side javascript raw source code.
var ClientSource = []byte(`var websocketStringMessageType = 0; var ClientSource = []byte(`var websocketStringMessageType = 0;
var websocketIntMessageType = 1; var websocketIntMessageType = 1;
var websocketBoolMessageType = 2; var websocketBoolMessageType = 2;
var websocketJSONMessageType = 4; var websocketJSONMessageType = 4;
var websocketMessagePrefix = "iris-websocket-message:"; var websocketMessagePrefix = "` + DefaultEvtMessageKey + `";
var websocketMessageSeparator = ";"; var websocketMessageSeparator = ";";
var websocketMessagePrefixLen = websocketMessagePrefix.length; var websocketMessagePrefixLen = websocketMessagePrefix.length;
var websocketMessageSeparatorLen = websocketMessageSeparator.length; var websocketMessageSeparatorLen = websocketMessageSeparator.length;

View File

@ -1,10 +1,13 @@
package websocket package websocket
import ( import (
"math/rand"
"net/http" "net/http"
"time" "time"
"github.com/kataras/iris/context" "github.com/kataras/iris/context"
"github.com/iris-contrib/go.uuid"
) )
const ( const (
@ -22,15 +25,24 @@ const (
DefaultWebsocketReadBufferSize = 4096 DefaultWebsocketReadBufferSize = 4096
// DefaultWebsocketWriterBufferSize 4096 // DefaultWebsocketWriterBufferSize 4096
DefaultWebsocketWriterBufferSize = 4096 DefaultWebsocketWriterBufferSize = 4096
// DefaultClientSourcePath "/iris-ws.js" // DefaultEvtMessageKey is the default prefix of the underline websocket events
DefaultClientSourcePath = "/iris-ws.js" // that are being established under the hoods.
//
// Defaults to "iris-websocket-message:".
// Last character of the prefix should be ':'.
DefaultEvtMessageKey = "iris-websocket-message:"
) )
var ( var (
// DefaultIDGenerator returns the result of 64 // DefaultIDGenerator returns a random unique for a new connection.
// random combined characters as the id of a new connection. // Used when config.IDGenerator is nil.
// Used when config.IDGenerator is nil DefaultIDGenerator = func(context.Context) string {
DefaultIDGenerator = func(context.Context) string { return randomString(64) } id, err := uuid.NewV4()
if err != nil {
return randomString(64)
}
return id.String()
}
) )
// Config the websocket server configuration // Config the websocket server configuration
@ -38,9 +50,16 @@ var (
type Config struct { type Config struct {
// IDGenerator used to create (and later on, set) // IDGenerator used to create (and later on, set)
// an ID for each incoming websocket connections (clients). // an ID for each incoming websocket connections (clients).
// The request is an argument which you can use to generate the ID (from headers for example). // The request is an input parameter which you can use to generate the ID (from headers for example).
// If empty then the ID is generated by DefaultIDGenerator: randomString(64) // If empty then the ID is generated by DefaultIDGenerator: randomString(64)
IDGenerator func(ctx context.Context) string IDGenerator func(ctx context.Context) string
// EvtMessagePrefix is the prefix of the underline websocket events that are being established under the hoods.
// This prefix is visible only to the javascript side (code) and it has nothing to do
// with the message that the end-user receives.
// Do not change it unless it is absolutely necessary.
//
// If empty then defaults to []byte("iris-websocket-message:").
EvtMessagePrefix []byte
// Error is the function that will be fired if any client couldn't upgrade the HTTP connection // Error is the function that will be fired if any client couldn't upgrade the HTTP connection
// to a websocket connection, a handshake error. // to a websocket connection, a handshake error.
Error func(w http.ResponseWriter, r *http.Request, status int, reason error) Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
@ -135,9 +154,47 @@ func (c Config) Validate() Config {
} }
} }
if len(c.EvtMessagePrefix) == 0 {
c.EvtMessagePrefix = []byte(DefaultEvtMessageKey)
}
if c.IDGenerator == nil { if c.IDGenerator == nil {
c.IDGenerator = DefaultIDGenerator c.IDGenerator = DefaultIDGenerator
} }
return c return c
} }
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
var src = rand.NewSource(time.Now().UnixNano())
// random takes a parameter (int) and returns random slice of byte
// ex: var randomstrbytes []byte; randomstrbytes = utils.Random(32)
func random(n int) []byte {
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return b
}
// randomString accepts a number(10 for example) and returns a random string using simple but fairly safe random algorithm
func randomString(n int) string {
return string(random(n))
}

View File

@ -415,15 +415,15 @@ func (c *connection) startReader() {
// messageReceived checks the incoming message and fire the nativeMessage listeners or the event listeners (ws custom message) // messageReceived checks the incoming message and fire the nativeMessage listeners or the event listeners (ws custom message)
func (c *connection) messageReceived(data []byte) { func (c *connection) messageReceived(data []byte) {
if bytes.HasPrefix(data, websocketMessagePrefixBytes) { if bytes.HasPrefix(data, c.server.config.EvtMessagePrefix) {
customData := string(data)
//it's a custom ws message //it's a custom ws message
receivedEvt := getWebsocketCustomEvent(customData) receivedEvt := c.server.messageSerializer.getWebsocketCustomEvent(data)
listeners := c.onEventListeners[receivedEvt] listeners, ok := c.onEventListeners[string(receivedEvt)]
if listeners == nil { // if not listeners for this event exit from here if !ok || len(listeners) == 0 {
return return // if not listeners for this event exit from here
} }
customMessage, err := websocketMessageDeserialize(receivedEvt, customData)
customMessage, err := c.server.messageSerializer.deserialize(receivedEvt, data)
if customMessage == nil || err != nil { if customMessage == nil || err != nil {
return return
} }

View File

@ -34,10 +34,10 @@ func (e *emitter) EmitMessage(nativeMessage []byte) error {
} }
func (e *emitter) Emit(event string, data interface{}) error { func (e *emitter) Emit(event string, data interface{}) error {
message, err := websocketMessageSerialize(event, data) message, err := e.conn.server.messageSerializer.serialize(event, data)
if err != nil { if err != nil {
return err return err
} }
e.EmitMessage([]byte(message)) e.EmitMessage(message)
return nil return nil
} }

View File

@ -1,179 +1,181 @@
package websocket package websocket
import ( import (
"bytes"
"encoding/binary"
"encoding/json" "encoding/json"
"math/rand"
"strconv" "strconv"
"strings"
"time"
"github.com/kataras/iris/core/errors" "github.com/kataras/iris/core/errors"
"github.com/valyala/bytebufferpool" "github.com/valyala/bytebufferpool"
) )
// The same values are exists on client side also
const (
websocketStringMessageType websocketMessageType = iota
websocketIntMessageType
websocketBoolMessageType
websocketBytesMessageType
websocketJSONMessageType
)
const (
websocketMessagePrefix = "iris-websocket-message:"
websocketMessageSeparator = ";"
websocketMessagePrefixLen = len(websocketMessagePrefix)
websocketMessageSeparatorLen = len(websocketMessageSeparator)
websocketMessagePrefixAndSepIdx = websocketMessagePrefixLen + websocketMessageSeparatorLen - 1
websocketMessagePrefixIdx = websocketMessagePrefixLen - 1
websocketMessageSeparatorIdx = websocketMessageSeparatorLen - 1
)
var (
websocketMessageSeparatorByte = websocketMessageSeparator[0]
websocketMessageBuffer = bytebufferpool.Pool{}
websocketMessagePrefixBytes = []byte(websocketMessagePrefix)
)
type ( type (
websocketMessageType uint8 messageType uint8
) )
func (m websocketMessageType) String() string { func (m messageType) String() string {
return strconv.Itoa(int(m)) return strconv.Itoa(int(m))
} }
func (m websocketMessageType) Name() string { func (m messageType) Name() string {
if m == websocketStringMessageType { switch m {
case messageTypeString:
return "string" return "string"
} else if m == websocketIntMessageType { case messageTypeInt:
return "int" return "int"
} else if m == websocketBoolMessageType { case messageTypeBool:
return "bool" return "bool"
} else if m == websocketBytesMessageType { case messageTypeBytes:
return "[]byte" return "[]byte"
} else if m == websocketJSONMessageType { case messageTypeJSON:
return "json" return "json"
default:
return "Invalid(" + m.String() + ")"
} }
return "Invalid(" + m.String() + ")"
} }
// The same values are exists on client side too.
const (
messageTypeString messageType = iota
messageTypeInt
messageTypeBool
messageTypeBytes
messageTypeJSON
)
const (
messageSeparator = ";"
)
var messageSeparatorByte = messageSeparator[0]
type messageSerializer struct {
prefix []byte
prefixLen int
separatorLen int
prefixAndSepIdx int
prefixIdx int
separatorIdx int
buf *bytebufferpool.Pool
}
func newMessageSerializer(messagePrefix []byte) *messageSerializer {
return &messageSerializer{
prefix: messagePrefix,
prefixLen: len(messagePrefix),
separatorLen: len(messageSeparator),
prefixAndSepIdx: len(messagePrefix) + len(messageSeparator) - 1,
prefixIdx: len(messagePrefix) - 1,
separatorIdx: len(messageSeparator) - 1,
buf: new(bytebufferpool.Pool),
}
}
var (
boolTrueB = []byte("true")
boolFalseB = []byte("false")
)
// websocketMessageSerialize serializes a custom websocket message from websocketServer to be delivered to the client // websocketMessageSerialize serializes a custom websocket message from websocketServer to be delivered to the client
// returns the string form of the message // returns the string form of the message
// Supported data types are: string, int, bool, bytes and JSON. // Supported data types are: string, int, bool, bytes and JSON.
func websocketMessageSerialize(event string, data interface{}) (string, error) { func (ms *messageSerializer) serialize(event string, data interface{}) ([]byte, error) {
var msgType websocketMessageType b := ms.buf.Get()
var dataMessage string b.Write(ms.prefix)
b.WriteString(event)
b.WriteByte(messageSeparatorByte)
if s, ok := data.(string); ok { switch v := data.(type) {
msgType = websocketStringMessageType case string:
dataMessage = s b.WriteString(messageTypeString.String())
} else if i, ok := data.(int); ok { b.WriteByte(messageSeparatorByte)
msgType = websocketIntMessageType b.WriteString(v)
dataMessage = strconv.Itoa(i) case int:
} else if b, ok := data.(bool); ok { b.WriteString(messageTypeInt.String())
msgType = websocketBoolMessageType b.WriteByte(messageSeparatorByte)
dataMessage = strconv.FormatBool(b) binary.Write(b, binary.LittleEndian, v)
} else if by, ok := data.([]byte); ok { case bool:
msgType = websocketBytesMessageType b.WriteString(messageTypeBool.String())
dataMessage = string(by) b.WriteByte(messageSeparatorByte)
} else { if v {
b.Write(boolTrueB)
} else {
b.Write(boolFalseB)
}
case []byte:
b.WriteString(messageTypeBytes.String())
b.WriteByte(messageSeparatorByte)
b.Write(v)
default:
//we suppose is json //we suppose is json
res, err := json.Marshal(data) res, err := json.Marshal(data)
if err != nil { if err != nil {
return "", err return nil, err
} }
msgType = websocketJSONMessageType b.WriteString(messageTypeJSON.String())
dataMessage = string(res) b.WriteByte(messageSeparatorByte)
b.Write(res)
} }
b := websocketMessageBuffer.Get() message := b.Bytes()
b.WriteString(websocketMessagePrefix) ms.buf.Put(b)
b.WriteString(event)
b.WriteString(websocketMessageSeparator)
b.WriteString(msgType.String())
b.WriteString(websocketMessageSeparator)
b.WriteString(dataMessage)
dataMessage = b.String()
websocketMessageBuffer.Put(b)
return dataMessage, nil
return message, nil
} }
var errInvalidTypeMessage = errors.New("Type %s is invalid for message: %s") var errInvalidTypeMessage = errors.New("Type %s is invalid for message: %s")
// websocketMessageDeserialize deserializes a custom websocket message from the client // deserialize deserializes a custom websocket message from the client
// ex: iris-websocket-message;chat;4;themarshaledstringfromajsonstruct will return 'hello' as string // ex: iris-websocket-message;chat;4;themarshaledstringfromajsonstruct will return 'hello' as string
// Supported data types are: string, int, bool, bytes and JSON. // Supported data types are: string, int, bool, bytes and JSON.
func websocketMessageDeserialize(event string, websocketMessage string) (message interface{}, err error) { func (ms *messageSerializer) deserialize(event []byte, websocketMessage []byte) (interface{}, error) {
t, formaterr := strconv.Atoi(websocketMessage[websocketMessagePrefixAndSepIdx+len(event)+1 : websocketMessagePrefixAndSepIdx+len(event)+2]) // in order to iris-websocket-message;user;-> 4 dataStartIdx := ms.prefixAndSepIdx + len(event) + 3
if formaterr != nil { if len(websocketMessage) <= dataStartIdx {
return nil, formaterr return nil, errors.New("websocket invalid message: " + string(websocketMessage))
}
_type := websocketMessageType(t)
_message := websocketMessage[websocketMessagePrefixAndSepIdx+len(event)+3:] // in order to iris-websocket-message;user;4; -> themarshaledstringfromajsonstruct
if _type == websocketStringMessageType {
message = string(_message)
} else if _type == websocketIntMessageType {
message, err = strconv.Atoi(_message)
} else if _type == websocketBoolMessageType {
message, err = strconv.ParseBool(_message)
} else if _type == websocketBytesMessageType {
message = []byte(_message)
} else if _type == websocketJSONMessageType {
err = json.Unmarshal([]byte(_message), &message)
} else {
return nil, errInvalidTypeMessage.Format(_type.Name(), websocketMessage)
} }
return typ, err := strconv.Atoi(string(websocketMessage[ms.prefixAndSepIdx+len(event)+1 : ms.prefixAndSepIdx+len(event)+2])) // in order to iris-websocket-message;user;-> 4
if err != nil {
return nil, err
}
data := websocketMessage[dataStartIdx:] // in order to iris-websocket-message;user;4; -> themarshaledstringfromajsonstruct
switch messageType(typ) {
case messageTypeString:
return string(data), nil
case messageTypeInt:
msg, err := strconv.Atoi(string(data))
if err != nil {
return nil, err
}
return msg, nil
case messageTypeBool:
if bytes.Equal(data, boolTrueB) {
return true, nil
}
return false, nil
case messageTypeBytes:
return data, nil
case messageTypeJSON:
var msg interface{}
err := json.Unmarshal(data, &msg)
return msg, err
default:
return nil, errInvalidTypeMessage.Format(messageType(typ).Name(), websocketMessage)
}
} }
// getWebsocketCustomEvent return empty string when the websocketMessage is native message // getWebsocketCustomEvent return empty string when the websocketMessage is native message
func getWebsocketCustomEvent(websocketMessage string) string { func (ms *messageSerializer) getWebsocketCustomEvent(websocketMessage []byte) []byte {
if len(websocketMessage) < websocketMessagePrefixAndSepIdx { if len(websocketMessage) < ms.prefixAndSepIdx {
return "" return nil
} }
s := websocketMessage[websocketMessagePrefixAndSepIdx:] s := websocketMessage[ms.prefixAndSepIdx:]
evt := s[:strings.IndexByte(s, websocketMessageSeparatorByte)] evt := s[:bytes.IndexByte(s, messageSeparatorByte)]
return evt return evt
} }
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
var src = rand.NewSource(time.Now().UnixNano())
// random takes a parameter (int) and returns random slice of byte
// ex: var randomstrbytes []byte; randomstrbytes = utils.Random(32)
func random(n int) []byte {
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return b
}
// randomString accepts a number(10 for example) and returns a random string using simple but fairly safe random algorithm
func randomString(n int) string {
return string(random(n))
}

View File

@ -1,6 +1,7 @@
package websocket package websocket
import ( import (
"bytes"
"sync" "sync"
"github.com/kataras/iris/context" "github.com/kataras/iris/context"
@ -34,7 +35,15 @@ type (
// //
// To serve the built'n javascript client-side library look the `websocket.ClientHandler`. // To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
Server struct { Server struct {
config Config config Config
// ClientSource contains the javascript side code
// for the iris websocket communication
// based on the configuration's `EvtMessagePrefix`.
//
// Use a route to serve this file on a specific path, i.e
// app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(mywebsocketServer.ClientSource) })
ClientSource []byte
messageSerializer *messageSerializer
connections sync.Map // key = the Connection ID. connections sync.Map // key = the Connection ID.
rooms map[string][]string // by default a connection is joined to a room which has the connection id as its name rooms map[string][]string // by default a connection is joined to a room which has the connection id as its name
mu sync.RWMutex // for rooms. mu sync.RWMutex // for rooms.
@ -53,6 +62,8 @@ func New(cfg Config) *Server {
cfg = cfg.Validate() cfg = cfg.Validate()
return &Server{ return &Server{
config: cfg, config: cfg,
ClientSource: bytes.Replace(ClientSource, []byte(DefaultEvtMessageKey), cfg.EvtMessagePrefix, -1),
messageSerializer: newMessageSerializer(cfg.EvtMessagePrefix),
connections: sync.Map{}, // ready-to-use, this is not necessary. connections: sync.Map{}, // ready-to-use, this is not necessary.
rooms: make(map[string][]string), rooms: make(map[string][]string),
onConnectionListeners: make([]ConnectionFunc, 0), onConnectionListeners: make([]ConnectionFunc, 0),