-
-
-
-
-
-
-
-
-
-
-
diff --git a/_examples/websocket/custom-go-client/main.go b/_examples/websocket/custom-go-client/main.go
deleted file mode 100644
index 7dba7e16..00000000
--- a/_examples/websocket/custom-go-client/main.go
+++ /dev/null
@@ -1,179 +0,0 @@
-package main
-
-// Run first `go run main.go server`
-// and `go run main.go client` as many times as you want.
-// Originally written by: github.com/antlaw to describe an old issue.
-import (
- "fmt"
- "os"
- "strings"
- "time"
-
- "github.com/kataras/iris"
- "github.com/kataras/iris/websocket"
-
- xwebsocket "golang.org/x/net/websocket"
-)
-
-// WS is the current websocket connection
-var WS *xwebsocket.Conn
-
-// $ go run main.go server
-// $ go run main.go client
-func main() {
- if len(os.Args) == 2 && strings.ToLower(os.Args[1]) == "server" {
- ServerLoop()
- } else if len(os.Args) == 2 && strings.ToLower(os.Args[1]) == "client" {
- ClientLoop()
- } else {
- fmt.Println("wsserver [server|client]")
- }
-}
-
-/////////////////////////////////////////////////////////////////////////
-// client side
-func sendUntilErr(sendInterval int) {
- i := 1
- for {
- time.Sleep(time.Duration(sendInterval) * time.Second)
- err := SendMessage("2", "all", "objectupdate", "2.UsrSchedule_v1_1")
- if err != nil {
- fmt.Println("failed to send join message", err.Error())
- return
- }
- fmt.Println("objectupdate", i)
- i++
- }
-}
-
-func recvUntilErr() {
- var msg = make([]byte, 2048)
- var n int
- var err error
- i := 1
- for {
- if n, err = WS.Read(msg); err != nil {
- fmt.Println(err.Error())
- return
- }
- fmt.Printf("%v Received: %s.%v\n", time.Now(), string(msg[:n]), i)
- i++
- }
-
-}
-
-//ConnectWebSocket connect a websocket to host
-func ConnectWebSocket() error {
- var origin = "http://localhost/"
- var url = "ws://localhost:8080/socket"
- var err error
- WS, err = xwebsocket.Dial(url, "", origin)
- return err
-}
-
-// CloseWebSocket closes the current websocket connection
-func CloseWebSocket() error {
- if WS != nil {
- return WS.Close()
- }
- return nil
-}
-
-// SendMessage broadcast a message to server
-func SendMessage(serverID, to, method, message string) error {
- buffer := []byte(message)
- return SendtBytes(serverID, to, method, buffer)
-}
-
-// SendtBytes broadcast a message to server
-func SendtBytes(serverID, to, method string, message []byte) error {
- // look https://github.com/kataras/iris/blob/master/websocket/message.go , client.js.go and client.js
- // to understand the buffer line:
- buffer := []byte(fmt.Sprintf("%s%v;0;%v;%v;", websocket.DefaultEvtMessageKey, method, serverID, to))
- buffer = append(buffer, message...)
- _, err := WS.Write(buffer)
- if err != nil {
- fmt.Println(err)
- return err
- }
- return nil
-}
-
-// ClientLoop connects to websocket server, the keep send and recv dataS
-func ClientLoop() {
- for {
- time.Sleep(time.Second)
- err := ConnectWebSocket()
- if err != nil {
- fmt.Println("failed to connect websocket", err.Error())
- continue
- }
- // time.Sleep(time.Second)
- err = SendMessage("2", "all", "join", "dummy2")
- go sendUntilErr(1)
- recvUntilErr()
- err = CloseWebSocket()
- if err != nil {
- fmt.Println("failed to close websocket", err.Error())
- }
- }
-
-}
-
-/////////////////////////////////////////////////////////////////////////
-// server side
-
-// OnConnect handles incoming websocket connection
-func OnConnect(c websocket.Connection) {
- fmt.Println("socket.OnConnect()")
- c.On("join", func(message string) { OnJoin(message, c) })
- c.On("objectupdate", func(message string) { OnObjectUpdated(message, c) })
- // ok works too c.EmitMessage([]byte("dsadsa"))
- c.OnDisconnect(func() { OnDisconnect(c) })
-
-}
-
-// ServerLoop listen and serve websocket requests
-func ServerLoop() {
- app := iris.New()
-
- ws := websocket.New(websocket.Config{})
-
- // register the server on an endpoint.
- // see the inline javascript code i the websockets.html, this endpoint is used to connect to the server.
- app.Get("/socket", ws.Handler())
-
- ws.OnConnection(OnConnect)
- app.Run(iris.Addr(":8080"))
-}
-
-// OnJoin handles Join broadcast group request
-func OnJoin(message string, c websocket.Connection) {
- t := time.Now()
- c.Join("server2")
- fmt.Println("OnJoin() time taken:", time.Since(t))
-}
-
-// OnObjectUpdated broadcasts to all client an incoming message
-func OnObjectUpdated(message string, c websocket.Connection) {
- t := time.Now()
- s := strings.Split(message, ";")
- if len(s) != 3 {
- fmt.Println("OnObjectUpdated() invalid message format:" + message)
- return
- }
- serverID, _, objectID := s[0], s[1], s[2]
- err := c.To("server"+serverID).Emit("objectupdate", objectID)
- if err != nil {
- fmt.Println(err, "failed to broacast object")
- return
- }
- fmt.Println(fmt.Sprintf("OnObjectUpdated() message:%v, time taken: %v", message, time.Since(t)))
-}
-
-// OnDisconnect clean up things when a client is disconnected
-func OnDisconnect(c websocket.Connection) {
- c.Leave("server2")
- fmt.Println("OnDisconnect(): client disconnected!")
-
-}
diff --git a/_examples/websocket/custom-go-client/run.bat b/_examples/websocket/custom-go-client/run.bat
deleted file mode 100644
index 3e483188..00000000
--- a/_examples/websocket/custom-go-client/run.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-@echo off
-REM run.bat 30
-start go run main.go server
-for /L %%n in (1,1,%1) do start go run main.go client
\ No newline at end of file
diff --git a/_examples/websocket/go-client/client/main.go b/_examples/websocket/go-client/client/main.go
deleted file mode 100644
index 6d3f744d..00000000
--- a/_examples/websocket/go-client/client/main.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package main
-
-import (
- "bufio"
- "fmt"
- "os"
-
- "github.com/kataras/iris/websocket"
-)
-
-const (
- url = "ws://localhost:8080/socket"
- prompt = ">> "
-)
-
-/*
-How to run:
-Start the server, if it is not already started by executing `go run ../server/main.go`
-And open two or more terminal windows and start the clients:
-$ go run main.go
->> hi!
-*/
-func main() {
- c, err := websocket.Dial(nil, url, websocket.ConnectionConfig{})
- if err != nil {
- panic(err)
- }
-
- c.OnError(func(err error) {
- fmt.Printf("error: %v", err)
- })
-
- c.OnDisconnect(func() {
- fmt.Println("Server was force-closed[see ../server/main.go#L17] this connection after 20 seconds, therefore I am disconnected.")
- os.Exit(0)
- })
-
- c.On("chat", func(message string) {
- fmt.Printf("\n%s\n", message)
- })
-
- fmt.Println("Start by typing a message to send")
- scanner := bufio.NewScanner(os.Stdin)
- for {
- fmt.Print(prompt)
- if !scanner.Scan() || scanner.Err() != nil {
- break
- }
- msgToSend := scanner.Text()
- if msgToSend == "exit" {
- break
- }
-
- c.Emit("chat", msgToSend)
- }
-
- fmt.Println("Terminated.")
-}
diff --git a/_examples/websocket/go-client/server/main.go b/_examples/websocket/go-client/server/main.go
deleted file mode 100644
index 0c6bed81..00000000
--- a/_examples/websocket/go-client/server/main.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/kataras/iris"
- "github.com/kataras/iris/websocket"
-)
-
-func main() {
- app := iris.New()
- ws := websocket.New(websocket.Config{})
- ws.OnConnection(func(c websocket.Connection) {
- go func() {
- <-time.After(20 * time.Second)
- c.Disconnect()
- }()
-
- c.On("chat", func(message string) {
- c.To(websocket.Broadcast).Emit("chat", c.ID()+": "+message)
- })
-
- c.OnDisconnect(func() {
- fmt.Printf("Connection with ID: %s has been disconnected!\n", c.ID())
- })
- })
-
- app.Get("/socket", ws.Handler())
-
- app.Run(iris.Addr(":8080"))
-}
diff --git a/go.mod b/go.mod
index 3199ff89..dc3ca374 100644
--- a/go.mod
+++ b/go.mod
@@ -1,57 +1,29 @@
module github.com/kataras/iris
+go 1.12
+
require (
- github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 // indirect
github.com/BurntSushi/toml v0.3.1
github.com/Joker/jade v1.0.0
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398
- github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f // indirect
github.com/aymerick/raymond v2.0.2+incompatible
- github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/dgraph-io/badger v1.5.4
- github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 // indirect
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385
- github.com/etcd-io/bbolt v1.3.0
github.com/fatih/structs v1.1.0
- github.com/flosch/pongo2 v0.0.0-20180809100617-24195e6d38b0
- github.com/gavv/monotime v0.0.0-20171021193802-6f8212e8d10d // indirect
- github.com/golang/protobuf v1.2.0 // indirect
- github.com/gomodule/redigo v2.0.0+incompatible
- github.com/google/go-querystring v1.0.0 // indirect
- github.com/gorilla/websocket v1.4.0
- github.com/hashicorp/go-version v1.0.0
- github.com/imkira/go-interpol v1.1.0 // indirect
+ github.com/flosch/pongo2 v0.0.0-20190505152737-8914e1cf9164
github.com/iris-contrib/blackfriday v2.0.0+incompatible
github.com/iris-contrib/formBinder v0.0.0-20190104093907-fbd5963f41e1
github.com/iris-contrib/go.uuid v2.0.0+incompatible
- github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce
- github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0
- github.com/json-iterator/go v1.1.5
- github.com/juju/errors v0.0.0-20181012004132-a4583d0a56ea // indirect
+ github.com/json-iterator/go v1.1.6
github.com/kataras/golog v0.0.0-20180321173939-03be10146386
+ github.com/kataras/neffos v0.0.0-20190602135205-38e9cc9b65c6
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d // indirect
- github.com/klauspost/compress v1.4.1
- github.com/klauspost/cpuid v1.2.0 // indirect
- github.com/microcosm-cc/bluemonday v1.0.1
- github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
- github.com/modern-go/reflect2 v1.0.1 // indirect
- github.com/moul/http2curl v1.0.0 // indirect
- github.com/pkg/errors v0.8.0 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/microcosm-cc/bluemonday v1.0.2
github.com/ryanuber/columnize v2.1.0+incompatible
- github.com/sergi/go-diff v1.0.0 // indirect
- github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect
- github.com/stretchr/testify v1.2.2 // indirect
- github.com/valyala/bytebufferpool v1.0.0
- github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
- github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
- github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56 // indirect
- github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
- github.com/yudai/gojsondiff v1.0.0 // indirect
- github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
- golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869
- golang.org/x/net v0.0.0-20181114220301-adae6a3d119a // indirect
- gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
- gopkg.in/ini.v1 v1.39.0 // indirect
- gopkg.in/yaml.v2 v2.2.1
+ github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
+ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5
+ golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect
+ golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed // indirect
+ golang.org/x/text v0.3.2 // indirect
+ golang.org/x/tools v0.0.0-20190602112858-2de7f9bf822c // indirect
+ gopkg.in/yaml.v2 v2.2.2
)
diff --git a/go.sum b/go.sum
index 5c1b1605..50cadf86 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,3 @@
-github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a/go.mod h1:MzD2WMdSxvbHw5fM/OXOFily/lipJWRc9C1px0Mt0ZE=
@@ -6,77 +5,63 @@ github.com/Joker/jade v1.0.0 h1:lOCEPvTAtWfLpSZYMOv/g44MGQFAolbKh2khHHGu0Kc=
github.com/Joker/jade v1.0.0/go.mod h1:efZIdO0py/LtcJRSa/j2WEklMSAw84WV0zZVMxNToB8=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
-github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0=
github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
-github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
-github.com/etcd-io/bbolt v1.3.0/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
-github.com/flosch/pongo2 v0.0.0-20180809100617-24195e6d38b0 h1:ZHx2BEERvWkuwuE7qWN9TuRxucHDH2JrsvneZjVJfo0=
-github.com/flosch/pongo2 v0.0.0-20180809100617-24195e6d38b0/go.mod h1:rE0ErqqBaMcp9pzj8JxV1GcfDBpuypXYxlR1c37AUwg=
-github.com/gavv/monotime v0.0.0-20171021193802-6f8212e8d10d/go.mod h1:vmp8DIyckQMXOPl0AQVHt+7n5h7Gb7hS6CUydiV8QeA=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/flosch/pongo2 v0.0.0-20190505152737-8914e1cf9164 h1:/HMcOGZC5Bi8JPgfbwz13ELWn/91+vY59HXS3z0qY5w=
+github.com/flosch/pongo2 v0.0.0-20190505152737-8914e1cf9164/go.mod h1:tbAXHifHQWNSpWbiJHpJTZH5fi3XHhDMdP//vuz9WS4=
+github.com/go-check/check v1.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
+github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
+github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.0.0 h1:1WdyfgUcImUfVBvYbsW2krIsnko+1QU2t45soaF8v1M=
+github.com/gobwas/ws v1.0.0/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4=
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
github.com/iris-contrib/formBinder v0.0.0-20190104093907-fbd5963f41e1 h1:7GsNnSLoVceNylMpwcfy5aFNz/S5/TV25crb34I5PEo=
github.com/iris-contrib/formBinder v0.0.0-20190104093907-fbd5963f41e1/go.mod h1:i8kTYUOEstd/S8TG0ChTXQdf4ermA/e8vJX0+QruD9w=
github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE=
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
-github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce/go.mod h1:VER17o2JZqquOx41avolD/wMGQSFEFBKWmhag9/RQRY=
-github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
-github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
-github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/juju/errors v0.0.0-20181012004132-a4583d0a56ea h1:g2k+8WR7cHch4g0tBDhfiEvAp7fXxTNBiD1oC1Oxj3E=
-github.com/juju/errors v0.0.0-20181012004132-a4583d0a56ea/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
+github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/kataras/golog v0.0.0-20180321173939-03be10146386 h1:VT6AeCHO/mc+VedKBMhoqb5eAK8B1i9F6nZl7EGlHvA=
github.com/kataras/golog v0.0.0-20180321173939-03be10146386/go.mod h1:PcaEvfvhGsqwXZ6S3CgCbmjcp+4UDUh2MIfF2ZEul8M=
+github.com/kataras/neffos v0.0.0-20190602135205-38e9cc9b65c6 h1:Kt26efzwR6OeuQ9IO8ufl6MjoJRvl0P6/fSnzHrW638=
+github.com/kataras/neffos v0.0.0-20190602135205-38e9cc9b65c6/go.mod h1:q/Hkityxm91OTjAXtQDTgaNhIrAe7JcDVDkvqSP+YGE=
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d h1:V5Rs9ztEWdp58oayPq/ulmlqJJZeJP6pP79uP3qjcao=
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
-github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E=
-github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
-github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/microcosm-cc/bluemonday v1.0.1 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44=
-github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
+github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY=
-github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
-github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
-github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
-github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
-golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 h1:kkXA53yGe04D0adEYJwEVQjeBppL01Exg+fnMjfUraU=
-golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/ini.v1 v1.39.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190602112858-2de7f9bf822c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/websocket/AUTHORS b/websocket/AUTHORS
deleted file mode 100644
index 7d08458d..00000000
--- a/websocket/AUTHORS
+++ /dev/null
@@ -1,4 +0,0 @@
-# This is the official list of Iris Websocket authors for copyright
-# purposes.
-
-Gerasimos Maropoulos
diff --git a/websocket/LICENSE b/websocket/LICENSE
deleted file mode 100644
index 1ea6d9b5..00000000
--- a/websocket/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (c) 2017-2019 The Iris Websocket Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Iris nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/websocket/client.js b/websocket/client.js
deleted file mode 100644
index ff323057..00000000
--- a/websocket/client.js
+++ /dev/null
@@ -1,208 +0,0 @@
-var websocketStringMessageType = 0;
-var websocketIntMessageType = 1;
-var websocketBoolMessageType = 2;
-var websocketJSONMessageType = 4;
-var websocketMessagePrefix = "iris-websocket-message:";
-var websocketMessageSeparator = ";";
-var websocketMessagePrefixLen = websocketMessagePrefix.length;
-var websocketMessageSeparatorLen = websocketMessageSeparator.length;
-var websocketMessagePrefixAndSepIdx = websocketMessagePrefixLen + websocketMessageSeparatorLen - 1;
-var websocketMessagePrefixIdx = websocketMessagePrefixLen - 1;
-var websocketMessageSeparatorIdx = websocketMessageSeparatorLen - 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) {
- return typeof obj === 'object';
- };
- //
- // messages
- Ws.prototype._msg = function (event, websocketMessageType, dataMessage) {
- return websocketMessagePrefix + event + websocketMessageSeparator + String(websocketMessageType) + websocketMessageSeparator + dataMessage;
- };
- Ws.prototype.encodeMessage = function (event, data) {
- var m = "";
- var t = 0;
- if (this.isNumber(data)) {
- t = websocketIntMessageType;
- m = data.toString();
- }
- else if (this.isBoolean(data)) {
- t = websocketBoolMessageType;
- m = data.toString();
- }
- else if (this.isString(data)) {
- t = websocketStringMessageType;
- m = data.toString();
- }
- else if (this.isJSON(data)) {
- //propably json-object
- t = websocketJSONMessageType;
- m = JSON.stringify(data);
- }
- else if (data !== null && typeof(data) !== "undefined" ) {
- // if it has a second parameter but it's not a type we know, then fire this:
- console.log("unsupported type of input argument passed, try to not include this argument to the 'Emit'");
- }
- return this._msg(event, t, m);
- };
- Ws.prototype.decodeMessage = function (event, websocketMessage) {
- //iris-websocket-message;user;4;themarshaledstringfromajsonstruct
- var skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
- if (websocketMessage.length < skipLen + 1) {
- return null;
- }
- var websocketMessageType = parseInt(websocketMessage.charAt(skipLen - 2));
- var theMessage = websocketMessage.substring(skipLen, websocketMessage.length);
- if (websocketMessageType == websocketIntMessageType) {
- return parseInt(theMessage);
- }
- else if (websocketMessageType == websocketBoolMessageType) {
- return Boolean(theMessage);
- }
- else if (websocketMessageType == websocketStringMessageType) {
- return theMessage;
- }
- else if (websocketMessageType == websocketJSONMessageType) {
- return JSON.parse(theMessage);
- }
- else {
- return null; // invalid
- }
- };
- Ws.prototype.getWebsocketCustomEvent = function (websocketMessage) {
- if (websocketMessage.length < websocketMessagePrefixAndSepIdx) {
- return "";
- }
- var s = websocketMessage.substring(websocketMessagePrefixAndSepIdx, websocketMessage.length);
- var evt = s.substring(0, s.indexOf(websocketMessageSeparator));
- return evt;
- };
- Ws.prototype.getCustomMessage = function (event, websocketMessage) {
- var eventIdx = websocketMessage.indexOf(event + websocketMessageSeparator);
- var s = websocketMessage.substring(eventIdx + event.length + websocketMessageSeparator.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 qws 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 qws message
- var message = evt.data;
- if (message.indexOf(websocketMessagePrefix) != -1) {
- var event_1 = this.getWebsocketCustomEvent(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;
-}());
\ No newline at end of file
diff --git a/websocket/client.js.go b/websocket/client.js.go
deleted file mode 100644
index 2144411a..00000000
--- a/websocket/client.js.go
+++ /dev/null
@@ -1,233 +0,0 @@
-package websocket
-
-import (
- "time"
-
- "github.com/kataras/iris/context"
-)
-
-// ClientHandler is the handler which serves the javascript client-side
-// library. It uses a small cache based on the iris/context.WriteWithExpiration.
-func ClientHandler() context.Handler {
- modNow := time.Now()
- return func(ctx context.Context) {
- ctx.ContentType("application/javascript")
- if _, err := ctx.WriteWithExpiration(ClientSource, modNow); err != nil {
- ctx.StatusCode(500)
- ctx.StopExecution()
- // ctx.Application().Logger().Infof("error while serving []byte via StaticContent: %s", err.Error())
- }
- }
-}
-
-// ClientSource the client-side javascript raw source code.
-var ClientSource = []byte(`var websocketStringMessageType = 0;
-var websocketIntMessageType = 1;
-var websocketBoolMessageType = 2;
-var websocketJSONMessageType = 4;
-var websocketMessagePrefix = "` + DefaultEvtMessageKey + `";
-var websocketMessageSeparator = ";";
-var websocketMessagePrefixLen = websocketMessagePrefix.length;
-var websocketMessageSeparatorLen = websocketMessageSeparator.length;
-var websocketMessagePrefixAndSepIdx = websocketMessagePrefixLen + websocketMessageSeparatorLen - 1;
-var websocketMessagePrefixIdx = websocketMessagePrefixLen - 1;
-var websocketMessageSeparatorIdx = websocketMessageSeparatorLen - 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) {
- return typeof obj === 'object';
- };
- //
- // messages
- Ws.prototype._msg = function (event, websocketMessageType, dataMessage) {
- return websocketMessagePrefix + event + websocketMessageSeparator + String(websocketMessageType) + websocketMessageSeparator + dataMessage;
- };
- Ws.prototype.encodeMessage = function (event, data) {
- var m = "";
- var t = 0;
- if (this.isNumber(data)) {
- t = websocketIntMessageType;
- m = data.toString();
- }
- else if (this.isBoolean(data)) {
- t = websocketBoolMessageType;
- m = data.toString();
- }
- else if (this.isString(data)) {
- t = websocketStringMessageType;
- m = data.toString();
- }
- else if (this.isJSON(data)) {
- //propably json-object
- t = websocketJSONMessageType;
- m = JSON.stringify(data);
- }
- else if (data !== null && typeof(data) !== "undefined" ) {
- // if it has a second parameter but it's not a type we know, then fire this:
- console.log("unsupported type of input argument passed, try to not include this argument to the 'Emit'");
- }
- return this._msg(event, t, m);
- };
- Ws.prototype.decodeMessage = function (event, websocketMessage) {
- //iris-websocket-message;user;4;themarshaledstringfromajsonstruct
- var skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
- if (websocketMessage.length < skipLen + 1) {
- return null;
- }
- var websocketMessageType = parseInt(websocketMessage.charAt(skipLen - 2));
- var theMessage = websocketMessage.substring(skipLen, websocketMessage.length);
- if (websocketMessageType == websocketIntMessageType) {
- return parseInt(theMessage);
- }
- else if (websocketMessageType == websocketBoolMessageType) {
- return Boolean(theMessage);
- }
- else if (websocketMessageType == websocketStringMessageType) {
- return theMessage;
- }
- else if (websocketMessageType == websocketJSONMessageType) {
- return JSON.parse(theMessage);
- }
- else {
- return null; // invalid
- }
- };
- Ws.prototype.getWebsocketCustomEvent = function (websocketMessage) {
- if (websocketMessage.length < websocketMessagePrefixAndSepIdx) {
- return "";
- }
- var s = websocketMessage.substring(websocketMessagePrefixAndSepIdx, websocketMessage.length);
- var evt = s.substring(0, s.indexOf(websocketMessageSeparator));
- return evt;
- };
- Ws.prototype.getCustomMessage = function (event, websocketMessage) {
- var eventIdx = websocketMessage.indexOf(event + websocketMessageSeparator);
- var s = websocketMessage.substring(eventIdx + event.length + websocketMessageSeparator.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 qws 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 qws message
- var message = evt.data;
- if (message.indexOf(websocketMessagePrefix) != -1) {
- var event_1 = this.getWebsocketCustomEvent(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;
-}());
-`)
diff --git a/websocket/client.min.js b/websocket/client.min.js
deleted file mode 100644
index 3d930f50..00000000
--- a/websocket/client.min.js
+++ /dev/null
@@ -1 +0,0 @@
-var websocketStringMessageType=0,websocketIntMessageType=1,websocketBoolMessageType=2,websocketJSONMessageType=4,websocketMessagePrefix="iris-websocket-message:",websocketMessageSeparator=";",websocketMessagePrefixLen=websocketMessagePrefix.length,websocketMessageSeparatorLen=websocketMessageSeparator.length,websocketMessagePrefixAndSepIdx=websocketMessagePrefixLen+websocketMessageSeparatorLen-1,websocketMessagePrefixIdx=websocketMessagePrefixLen-1,websocketMessageSeparatorIdx=websocketMessageSeparatorLen-1,Ws=function(){function e(e,s){var t=this;this.connectListeners=[],this.disconnectListeners=[],this.nativeMessageListeners=[],this.messageListeners={},window.WebSocket&&(-1==e.indexOf("ws")&&(e="ws://"+e),null!=s&&0 void;
-type onWebsocketDisconnectFunc = () => void;
-type onWebsocketNativeMessageFunc = (websocketMessage: string) => void;
-type onMessageFunc = (message: any) => void;
-
-class Ws {
- private conn: WebSocket;
- private isReady: boolean;
-
- // events listeners
-
- private connectListeners: onConnectFunc[] = [];
- private disconnectListeners: onWebsocketDisconnectFunc[] = [];
- private nativeMessageListeners: onWebsocketNativeMessageFunc[] = [];
- 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 {
- return typeof obj === 'object';
- }
-
- //
-
- // messages
- private _msg(event: string, websocketMessageType: number, dataMessage: string): string {
-
- return websocketMessagePrefix + event + websocketMessageSeparator + String(websocketMessageType) + websocketMessageSeparator + dataMessage;
- }
-
- private encodeMessage(event: string, data: any): string {
- let m = "";
- let t = 0;
- if (this.isNumber(data)) {
- t = websocketIntMessageType;
- m = data.toString();
- } else if (this.isBoolean(data)) {
- t = websocketBoolMessageType;
- m = data.toString();
- } else if (this.isString(data)) {
- t = websocketStringMessageType;
- m = data.toString();
- } else if (this.isJSON(data)) {
- //propably json-object
- t = websocketJSONMessageType;
- m = JSON.stringify(data);
- } else if (data !== null && typeof (data) !== "undefined") {
- // if it has a second parameter but it's not a type we know, then fire this:
- console.log("unsupported type of input argument passed, try to not include this argument to the 'Emit'");
- }
-
- return this._msg(event, t, m);
- }
-
- private decodeMessage(event: string, websocketMessage: string): T | any {
- //iris-websocket-message;user;4;themarshaledstringfromajsonstruct
- let skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
- if (websocketMessage.length < skipLen + 1) {
- return null;
- }
- let websocketMessageType = parseInt(websocketMessage.charAt(skipLen - 2));
- let theMessage = websocketMessage.substring(skipLen, websocketMessage.length);
- if (websocketMessageType == websocketIntMessageType) {
- return parseInt(theMessage);
- } else if (websocketMessageType == websocketBoolMessageType) {
- return Boolean(theMessage);
- } else if (websocketMessageType == websocketStringMessageType) {
- return theMessage;
- } else if (websocketMessageType == websocketJSONMessageType) {
- return JSON.parse(theMessage);
- } else {
- return null; // invalid
- }
- }
-
- private getWebsocketCustomEvent(websocketMessage: string): string {
- if (websocketMessage.length < websocketMessagePrefixAndSepIdx) {
- return "";
- }
- let s = websocketMessage.substring(websocketMessagePrefixAndSepIdx, websocketMessage.length);
- let evt = s.substring(0, s.indexOf(websocketMessageSeparator));
-
- return evt;
- }
-
- private getCustomMessage(event: string, websocketMessage: string): string {
- let eventIdx = websocketMessage.indexOf(event + websocketMessageSeparator);
- let s = websocketMessage.substring(eventIdx + event.length + websocketMessageSeparator.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 qws 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 qws message
- let message = evt.data;
- if (message.indexOf(websocketMessagePrefix) != -1) {
- let event = this.getWebsocketCustomEvent(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: onWebsocketDisconnectFunc): void {
- this.disconnectListeners.push(fn);
- }
-
- fireDisconnect(): void {
- for (let i = 0; i < this.disconnectListeners.length; i++) {
- this.disconnectListeners[i]();
- }
- }
-
- OnMessage(cb: onWebsocketNativeMessageFunc): 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};
diff --git a/websocket/config.go b/websocket/config.go
deleted file mode 100644
index 0636ae53..00000000
--- a/websocket/config.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package websocket
-
-import (
- "net/http"
- "strconv"
- "time"
-
- "github.com/kataras/iris/context"
-
- "github.com/iris-contrib/go.uuid"
-)
-
-const (
- // DefaultWebsocketWriteTimeout 0, no timeout
- DefaultWebsocketWriteTimeout = 0
- // DefaultWebsocketReadTimeout 0, no timeout
- DefaultWebsocketReadTimeout = 0
- // DefaultWebsocketPingPeriod is 0 but
- // could be 10 * time.Second.
- DefaultWebsocketPingPeriod = 0
- // DefaultWebsocketMaxMessageSize 0
- DefaultWebsocketMaxMessageSize = 0
- // DefaultWebsocketReadBufferSize 0
- DefaultWebsocketReadBufferSize = 0
- // DefaultWebsocketWriterBufferSize 0
- DefaultWebsocketWriterBufferSize = 0
- // DefaultEvtMessageKey is the default prefix of the underline websocket events
- // that are being established under the hoods.
- //
- // Defaults to "iris-websocket-message:".
- // Last character of the prefix should be ':'.
- DefaultEvtMessageKey = "iris-websocket-message:"
-)
-
-var (
- // DefaultIDGenerator returns a random unique string for a new connection.
- // Used when config.IDGenerator is nil.
- DefaultIDGenerator = func(context.Context) string {
- id, err := uuid.NewV4()
- if err != nil {
- return strconv.FormatInt(time.Now().Unix(), 10)
- }
- return id.String()
- }
-)
-
-// Config contains the websocket server's configuration, optional.
-type Config struct {
- // IDGenerator used to create (and later on, set)
- // an ID for each incoming websocket connections (clients).
- // 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)
- 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
- // to a websocket connection, a handshake error.
- Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
- // CheckOrigin a function that is called right before the handshake,
- // if returns false then that client is not allowed to connect with the websocket server.
- CheckOrigin func(r *http.Request) bool
- // HandshakeTimeout specifies the duration for the handshake to complete.
- HandshakeTimeout time.Duration
- // WriteTimeout time allowed to write a message to the connection.
- // 0 means no timeout.
- // Default value is 0
- WriteTimeout time.Duration
- // ReadTimeout time allowed to read a message from the connection.
- // 0 means no timeout.
- // Default value is 0
- ReadTimeout time.Duration
- // PingPeriod send ping messages to the connection repeatedly after this period.
- // The value should be close to the ReadTimeout to avoid issues.
- // Default value is 0.
- PingPeriod time.Duration
- // MaxMessageSize max message size allowed from connection.
- // Default value is 1024
- MaxMessageSize int64
- // BinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
- // compatible if you wanna use the Connection's EmitMessage to send a custom binary data to the client, like a native server-client communication.
- // Default value is false
- BinaryMessages bool
- // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
- // size is zero, then buffers allocated by the HTTP server are used. The
- // I/O buffer sizes do not limit the size of the messages that can be sent
- // or received.
- //
- // Default value is 0.
- ReadBufferSize, WriteBufferSize int
- // EnableCompression specify if the server should attempt to negotiate per
- // message compression (RFC 7692). Setting this value to true does not
- // guarantee that compression will be supported. Currently only "no context
- // takeover" modes are supported.
- //
- // Defaults to false and it should be remain as it is, unless special requirements.
- EnableCompression bool
-
- // Subprotocols specifies the server's supported protocols in order of
- // preference. If this field is set, then the Upgrade method negotiates a
- // subprotocol by selecting the first match in this list with a protocol
- // requested by the client.
- Subprotocols []string
-}
-
-// Validate validates the configuration
-func (c Config) Validate() Config {
- // 0 means no timeout.
- if c.WriteTimeout < 0 {
- c.WriteTimeout = DefaultWebsocketWriteTimeout
- }
-
- if c.ReadTimeout < 0 {
- c.ReadTimeout = DefaultWebsocketReadTimeout
- }
-
- if c.PingPeriod <= 0 {
- c.PingPeriod = DefaultWebsocketPingPeriod
- }
-
- if c.MaxMessageSize <= 0 {
- c.MaxMessageSize = DefaultWebsocketMaxMessageSize
- }
-
- if c.ReadBufferSize <= 0 {
- c.ReadBufferSize = DefaultWebsocketReadBufferSize
- }
-
- if c.WriteBufferSize <= 0 {
- c.WriteBufferSize = DefaultWebsocketWriterBufferSize
- }
-
- if c.Error == nil {
- c.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {
- //empty
- }
- }
-
- if c.CheckOrigin == nil {
- c.CheckOrigin = func(r *http.Request) bool {
- // allow all connections by default
- return true
- }
- }
-
- if len(c.EvtMessagePrefix) == 0 {
- c.EvtMessagePrefix = []byte(DefaultEvtMessageKey)
- }
-
- if c.IDGenerator == nil {
- c.IDGenerator = DefaultIDGenerator
- }
-
- return c
-}
diff --git a/websocket/connection.go b/websocket/connection.go
deleted file mode 100644
index ef6ef3a0..00000000
--- a/websocket/connection.go
+++ /dev/null
@@ -1,689 +0,0 @@
-package websocket
-
-import (
- "bytes"
- stdContext "context"
- "errors"
- "net"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/gorilla/websocket"
- "github.com/kataras/iris/context"
-)
-
-const (
- // TextMessage denotes a text data message. The text message payload is
- // interpreted as UTF-8 encoded text data.
- TextMessage = websocket.TextMessage
-
- // BinaryMessage denotes a binary data message.
- BinaryMessage = websocket.BinaryMessage
-
- // CloseMessage denotes a close control message. The optional message
- // payload contains a numeric code and text. Use the FormatCloseMessage
- // function to format a close message payload.
- CloseMessage = websocket.CloseMessage
-
- // PingMessage denotes a ping control message. The optional message payload
- // is UTF-8 encoded text.
- PingMessage = websocket.PingMessage
-
- // PongMessage denotes a ping control message. The optional message payload
- // is UTF-8 encoded text.
- PongMessage = websocket.PongMessage
-)
-
-type (
- connectionValue struct {
- key []byte
- value interface{}
- }
-)
-
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-// -------------------------------Connection implementation-----------------------------
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-
-type (
- // DisconnectFunc is the callback which is fired when a client/connection closed
- DisconnectFunc func()
- // LeaveRoomFunc is the callback which is fired 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 whenever an error occurs
- ErrorFunc (func(error))
- // 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{}
- // PingFunc is the callback which fires each ping
- PingFunc func()
- // PongFunc is the callback which fires on pong message received
- PongFunc func()
- // Connection is the front-end API that you will use to communicate with the client side,
- // it is the server-side connection.
- Connection interface {
- ClientConnection
- // Err is not nil if the upgrader failed to upgrade http to websocket connection.
- Err() error
- // ID returns the connection's identifier
- ID() string
- // Server returns the websocket server instance
- // which this connection is listening to.
- //
- // Its connection-relative operations are safe for use.
- Server() *Server
- // Context returns the (upgraded) context.Context of this connection
- // avoid using it, you normally don't need it,
- // 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() context.Context
- // To defines on what "room" (see Join) the server should send a message
- // returns an Emitter(`EmitMessage` & `Emit`) to send messages.
- To(string) Emitter
- // Join registers this connection to a room, if it doesn't exist then it creates a new. One room can have one or more connections. One connection can be joined to many rooms. All connections are joined to a room specified by their `ID` automatically.
- Join(string)
- // IsJoined returns true when this connection is joined to the room, otherwise false.
- // It Takes the room name as its input parameter.
- IsJoined(roomName string) bool
- // Leave removes this connection entry from a room
- // Returns true if the connection has actually left from the particular room.
- Leave(string) bool
- // OnLeave registers 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 theoretical can still send messages to its room right before it is being disconnected.
- OnLeave(roomLeaveCb LeaveRoomFunc)
- }
-
- // ClientConnection is the client-side connection interface. Server shares some of its methods but the underline actions differs.
- ClientConnection interface {
- Emitter
- // Write writes a raw websocket message with a specific type to the client
- // used by ping messages and any CloseMessage types.
- Write(websocketMessageType int, data []byte) error
- // OnMessage registers a callback which fires when native websocket message received
- OnMessage(NativeMessageFunc)
- // On registers a callback to a particular event which is fired when a message to this event is received
- On(string, MessageFunc)
- // OnError registers a callback which fires when this connection occurs an error
- OnError(ErrorFunc)
- // OnPing registers a callback which fires on each ping
- OnPing(PingFunc)
- // OnPong registers a callback which fires on pong message received
- OnPong(PongFunc)
- // FireOnError can be used to send a custom error message to the connection
- //
- // It does nothing more than firing the OnError listeners. It doesn't send anything to the client.
- FireOnError(err error)
- // OnDisconnect registers a callback which is fired when this connection is closed by an error or manual
- OnDisconnect(DisconnectFunc)
- // 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
- // Wait starts the pinger and the messages reader,
- // it's named as "Wait" because it should be called LAST,
- // after the "On" events IF server's `Upgrade` is used,
- // otherwise you don't have to call it because the `Handler()` does it automatically.
- Wait()
- // UnderlyingConn returns the underline gorilla websocket connection.
- UnderlyingConn() *websocket.Conn
- }
-
- connection struct {
- err error
- underline *websocket.Conn
- config ConnectionConfig
- defaultMessageType int
- serializer *messageSerializer
- id string
-
- onErrorListeners []ErrorFunc
- onPingListeners []PingFunc
- onPongListeners []PongFunc
- onNativeMessageListeners []NativeMessageFunc
- onEventListeners map[string][]MessageFunc
- onRoomLeaveListeners []LeaveRoomFunc
- onDisconnectListeners []DisconnectFunc
- disconnected uint32
-
- started bool
- // these were maden for performance only
- self Emitter // pre-defined emitter than sends message to its self client
- broadcast Emitter // pre-defined emitter that sends message to all except this
- all Emitter // pre-defined emitter which sends message to all clients
-
- // access to the Context, use with caution, you can't use response writer as you imagine.
- ctx context.Context
- 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.
- writerMu sync.Mutex
- // same exists for reader look here: https://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages
- // but we only use one reader in one goroutine, so we are safe.
- // readerMu sync.Mutex
- }
-)
-
-var _ Connection = &connection{}
-
-// WrapConnection wraps the underline websocket connection into a new iris websocket connection.
-// The caller should call the `connection#Wait` (which blocks) to enable its read and write functionality.
-func WrapConnection(underlineConn *websocket.Conn, cfg ConnectionConfig) Connection {
- return newConnection(underlineConn, cfg)
-}
-
-func newConnection(underlineConn *websocket.Conn, cfg ConnectionConfig) *connection {
- cfg = cfg.Validate()
- c := &connection{
- underline: underlineConn,
- config: cfg,
- serializer: newMessageSerializer(cfg.EvtMessagePrefix),
- defaultMessageType: websocket.TextMessage,
- onErrorListeners: make([]ErrorFunc, 0),
- onPingListeners: make([]PingFunc, 0),
- onPongListeners: make([]PongFunc, 0),
- onNativeMessageListeners: make([]NativeMessageFunc, 0),
- onEventListeners: make(map[string][]MessageFunc, 0),
- onDisconnectListeners: make([]DisconnectFunc, 0),
- disconnected: 0,
- }
-
- if cfg.BinaryMessages {
- c.defaultMessageType = websocket.BinaryMessage
- }
-
- return c
-}
-
-func newServerConnection(ctx context.Context, s *Server, underlineConn *websocket.Conn, id string) *connection {
- c := newConnection(underlineConn, ConnectionConfig{
- EvtMessagePrefix: s.config.EvtMessagePrefix,
- WriteTimeout: s.config.WriteTimeout,
- ReadTimeout: s.config.ReadTimeout,
- PingPeriod: s.config.PingPeriod,
- MaxMessageSize: s.config.MaxMessageSize,
- BinaryMessages: s.config.BinaryMessages,
- ReadBufferSize: s.config.ReadBufferSize,
- WriteBufferSize: s.config.WriteBufferSize,
- EnableCompression: s.config.EnableCompression,
- })
-
- c.id = id
- c.server = s
- c.ctx = ctx
- c.onRoomLeaveListeners = make([]LeaveRoomFunc, 0)
- c.started = false
-
- c.self = newEmitter(c, c.id)
- c.broadcast = newEmitter(c, Broadcast)
- c.all = newEmitter(c, All)
-
- return c
-}
-
-func (c *connection) UnderlyingConn() *websocket.Conn {
- return c.underline
-}
-
-// Err is not nil if the upgrader failed to upgrade http to websocket connection.
-func (c *connection) Err() error {
- return c.err
-}
-
-// Write writes a raw websocket message with a specific type to the client
-// used by ping messages and any CloseMessage types.
-func (c *connection) Write(websocketMessageType int, data []byte) error {
- // for any-case the app tries to write from different goroutines,
- // we must protect them because they're reporting that as bug...
- c.writerMu.Lock()
- if writeTimeout := c.config.WriteTimeout; writeTimeout > 0 {
- // set the write deadline based on the configuration
- c.underline.SetWriteDeadline(time.Now().Add(writeTimeout))
- }
-
- // .WriteMessage same as NextWriter and close (flush)
- err := c.underline.WriteMessage(websocketMessageType, data)
- c.writerMu.Unlock()
- if err != nil {
- // if failed then the connection is off, fire the disconnect
- c.Disconnect()
- }
- return err
-}
-
-// writeDefault is the same as write but the message type is the configured by c.messageType
-// if BinaryMessages is enabled then it's raw []byte as you expected to work with protobufs
-func (c *connection) writeDefault(data []byte) error {
- return c.Write(c.defaultMessageType, data)
-}
-
-const (
- // WriteWait is 1 second at the internal implementation,
- // same as here but this can be changed at the future*
- WriteWait = 1 * time.Second
-)
-
-func (c *connection) startPinger() {
-
- // this is the default internal handler, we just change the writeWait because of the actions we must do before
- // the server sends the ping-pong.
-
- pingHandler := func(message string) error {
- err := c.underline.WriteControl(websocket.PongMessage, []byte(message), time.Now().Add(WriteWait))
- if err == websocket.ErrCloseSent {
- return nil
- } else if e, ok := err.(net.Error); ok && e.Temporary() {
- return nil
- }
- return err
- }
-
- c.underline.SetPingHandler(pingHandler)
-
- if c.config.PingPeriod > 0 {
- go func() {
- for {
- time.Sleep(c.config.PingPeriod)
- if c == nil || atomic.LoadUint32(&c.disconnected) > 0 {
- // verifies if already disconnected.
- return
- }
- //fire all OnPing methods
- c.fireOnPing()
- // try to ping the client, if failed then it disconnects.
- err := c.Write(websocket.PingMessage, []byte{})
- if err != nil {
- // must stop to exit the loop and exit from the routine.
- return
- }
- }
- }()
- }
-}
-
-func (c *connection) fireOnPing() {
- // fire the onPingListeners
- for i := range c.onPingListeners {
- c.onPingListeners[i]()
- }
-}
-
-func (c *connection) fireOnPong() {
- // fire the onPongListeners
- for i := range c.onPongListeners {
- c.onPongListeners[i]()
- }
-}
-
-func (c *connection) startReader() {
- conn := c.underline
- hasReadTimeout := c.config.ReadTimeout > 0
-
- conn.SetReadLimit(c.config.MaxMessageSize)
- conn.SetPongHandler(func(s string) error {
- if hasReadTimeout {
- conn.SetReadDeadline(time.Now().Add(c.config.ReadTimeout))
- }
- //fire all OnPong methods
- go c.fireOnPong()
-
- return nil
- })
-
- defer func() {
- c.Disconnect()
- }()
-
- for {
- if hasReadTimeout {
- // set the read deadline based on the configuration
- conn.SetReadDeadline(time.Now().Add(c.config.ReadTimeout))
- }
-
- _, data, err := conn.ReadMessage()
- if err != nil {
- if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure, websocket.CloseNormalClosure) {
- c.FireOnError(err)
- }
- return
- }
-
- c.messageReceived(data)
- }
-
-}
-
-// messageReceived checks the incoming message and fire the nativeMessage listeners or the event listeners (ws custom message)
-func (c *connection) messageReceived(data []byte) {
-
- if bytes.HasPrefix(data, c.config.EvtMessagePrefix) {
- //it's a custom ws message
- receivedEvt := c.serializer.getWebsocketCustomEvent(data)
- listeners, ok := c.onEventListeners[string(receivedEvt)]
- if !ok || len(listeners) == 0 {
- return // if not listeners for this event exit from here
- }
-
- customMessage, err := c.serializer.deserialize(receivedEvt, data)
- 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) Server() *Server {
- return c.server
-}
-
-func (c *connection) Context() context.Context {
- return c.ctx
-}
-
-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) OnPing(cb PingFunc) {
- c.onPingListeners = append(c.onPingListeners, cb)
-}
-
-func (c *connection) OnPong(cb PongFunc) {
- c.onPongListeners = append(c.onPongListeners, cb)
-}
-
-func (c *connection) FireOnError(err error) {
- for _, cb := range c.onErrorListeners {
- cb(err)
- }
-}
-
-func (c *connection) To(to string) Emitter {
- if to == Broadcast { // if send to all except me, then return the pre-defined emitter, and so on
- return c.broadcast
- } else if to == All {
- return c.all
- } else if to == c.id {
- return c.self
- }
-
- // is an emitter to another client/connection
- return newEmitter(c, to)
-}
-
-func (c *connection) EmitMessage(nativeMessage []byte) error {
- if c.server != nil {
- return c.self.EmitMessage(nativeMessage)
- }
- return c.writeDefault(nativeMessage)
-}
-
-func (c *connection) Emit(event string, message interface{}) error {
- if c.server != nil {
- return c.self.Emit(event, message)
- }
-
- b, err := c.serializer.serialize(event, message)
- if err != nil {
- return err
- }
-
- return c.EmitMessage(b)
-}
-
-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) {
- c.server.Join(roomName, c.id)
-}
-
-func (c *connection) IsJoined(roomName string) bool {
- return c.server.IsJoined(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) {
- // check if connection is already closed
- if c == nil {
- return
- }
- // fire the onRoomLeaveListeners
- for i := range c.onRoomLeaveListeners {
- c.onRoomLeaveListeners[i](roomName)
- }
-}
-
-// Wait starts the pinger and the messages reader,
-// it's named as "Wait" because it should be called LAST,
-// after the "On" events IF server's `Upgrade` is used,
-// otherwise you don't have to call it because the `Handler()` does it automatically.
-func (c *connection) Wait() {
- // if c.server != nil && c.server.config.MaxConcurrentConnections > 0 {
- // defer func() {
- // go func() {
- // c.server.threads <- struct{}{}
- // }()
- // }()
- // }
-
- if c.started {
- return
- }
- c.started = true
- // start the ping
- c.startPinger()
-
- // start the messages reader
- c.startReader()
-}
-
-// ErrAlreadyDisconnected can be reported on the `Connection#Disconnect` function whenever the caller tries to close the
-// connection when it is already closed by the client or the caller previously.
-var ErrAlreadyDisconnected = errors.New("already disconnected")
-
-func (c *connection) Disconnect() error {
- if c == nil || !atomic.CompareAndSwapUint32(&c.disconnected, 0, 1) {
- return ErrAlreadyDisconnected
- }
-
- if c.server != nil {
- return c.server.Disconnect(c.ID())
- }
-
- err := c.underline.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
- if err != nil {
- err = c.underline.Close()
- }
-
- if err == nil {
- c.fireDisconnect()
- }
-
- return err
-}
-
-// ConnectionConfig is the base configuration for both server and client connections.
-// Clients must use `ConnectionConfig` in order to `Dial`, server's connection configuration is set by the `Config` structure.
-type ConnectionConfig struct {
- // 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:").
- // Should match with the server's EvtMessagePrefix.
- EvtMessagePrefix []byte
- // WriteTimeout time allowed to write a message to the connection.
- // 0 means no timeout.
- // Default value is 0
- WriteTimeout time.Duration
- // ReadTimeout time allowed to read a message from the connection.
- // 0 means no timeout.
- // Default value is 0
- ReadTimeout time.Duration
- // PingPeriod send ping messages to the connection repeatedly after this period.
- // The value should be close to the ReadTimeout to avoid issues.
- // Default value is 0
- PingPeriod time.Duration
- // MaxMessageSize max message size allowed from connection.
- // Default value is 0. Unlimited but it is recommended to be 1024 for medium to large messages.
- MaxMessageSize int64
- // BinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
- // compatible if you wanna use the Connection's EmitMessage to send a custom binary data to the client, like a native server-client communication.
- // Default value is false
- BinaryMessages bool
- // ReadBufferSize is the buffer size for the connection reader.
- // Default value is 4096
- ReadBufferSize int
- // WriteBufferSize is the buffer size for the connection writer.
- // Default value is 4096
- WriteBufferSize int
- // EnableCompression specify if the server should attempt to negotiate per
- // message compression (RFC 7692). Setting this value to true does not
- // guarantee that compression will be supported. Currently only "no context
- // takeover" modes are supported.
- //
- // Defaults to false and it should be remain as it is, unless special requirements.
- EnableCompression bool
-}
-
-// Validate validates the connection configuration.
-func (c ConnectionConfig) Validate() ConnectionConfig {
- if len(c.EvtMessagePrefix) == 0 {
- c.EvtMessagePrefix = []byte(DefaultEvtMessageKey)
- }
-
- // 0 means no timeout.
- if c.WriteTimeout < 0 {
- c.WriteTimeout = DefaultWebsocketWriteTimeout
- }
-
- if c.ReadTimeout < 0 {
- c.ReadTimeout = DefaultWebsocketReadTimeout
- }
-
- if c.PingPeriod <= 0 {
- c.PingPeriod = DefaultWebsocketPingPeriod
- }
-
- if c.MaxMessageSize <= 0 {
- c.MaxMessageSize = DefaultWebsocketMaxMessageSize
- }
-
- if c.ReadBufferSize <= 0 {
- c.ReadBufferSize = DefaultWebsocketReadBufferSize
- }
-
- if c.WriteBufferSize <= 0 {
- c.WriteBufferSize = DefaultWebsocketWriterBufferSize
- }
-
- return c
-}
-
-// ErrBadHandshake is returned when the server response to opening handshake is
-// invalid.
-var ErrBadHandshake = websocket.ErrBadHandshake
-
-// Dial creates a new client connection.
-//
-// The context will be used in the request and in the Dialer.
-//
-// If the WebSocket handshake fails, `ErrBadHandshake` is returned.
-//
-// The "url" input parameter is the url to connect to the server, it should be
-// the ws:// (or wss:// if secure) + the host + the endpoint of the
-// open socket of the server, i.e ws://localhost:8080/my_websocket_endpoint.
-//
-// Custom dialers can be used by wrapping the iris websocket connection via `websocket.WrapConnection`.
-func Dial(ctx stdContext.Context, url string, cfg ConnectionConfig) (ClientConnection, error) {
- if ctx == nil {
- ctx = stdContext.Background()
- }
-
- if !strings.HasPrefix(url, "ws://") && !strings.HasPrefix(url, "wss://") {
- url = "ws://" + url
- }
-
- conn, _, err := websocket.DefaultDialer.DialContext(ctx, url, nil)
- if err != nil {
- return nil, err
- }
-
- clientConn := WrapConnection(conn, cfg)
- go clientConn.Wait()
-
- return clientConn, nil
-}
diff --git a/websocket/emitter.go b/websocket/emitter.go
deleted file mode 100644
index 84d1fa48..00000000
--- a/websocket/emitter.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package websocket
-
-const (
- // All is the string which the Emitter use to send a message to all.
- All = ""
- // Broadcast is the string which the Emitter use to send a message to all except this connection.
- Broadcast = ";to;all;except;me;"
-)
-
-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{}
-
-func newEmitter(c *connection, to string) *emitter {
- return &emitter{conn: c, to: to}
-}
-
-func (e *emitter) EmitMessage(nativeMessage []byte) error {
- e.conn.server.emitMessage(e.conn.id, e.to, nativeMessage)
- return nil
-}
-
-func (e *emitter) Emit(event string, data interface{}) error {
- message, err := e.conn.serializer.serialize(event, data)
- if err != nil {
- return err
- }
- e.EmitMessage(message)
- return nil
-}
diff --git a/websocket/message.go b/websocket/message.go
deleted file mode 100644
index 6b27fbee..00000000
--- a/websocket/message.go
+++ /dev/null
@@ -1,182 +0,0 @@
-package websocket
-
-import (
- "bytes"
- "encoding/binary"
- "encoding/json"
- "strconv"
-
- "github.com/kataras/iris/core/errors"
- "github.com/valyala/bytebufferpool"
-)
-
-type (
- messageType uint8
-)
-
-func (m messageType) String() string {
- return strconv.Itoa(int(m))
-}
-
-func (m messageType) Name() string {
- switch m {
- case messageTypeString:
- return "string"
- case messageTypeInt:
- return "int"
- case messageTypeBool:
- return "bool"
- case messageTypeBytes:
- return "[]byte"
- case messageTypeJSON:
- return "json"
- default:
- 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
-// returns the string form of the message
-// Supported data types are: string, int, bool, bytes and JSON.
-func (ms *messageSerializer) serialize(event string, data interface{}) ([]byte, error) {
- b := ms.buf.Get()
- b.Write(ms.prefix)
- b.WriteString(event)
- b.WriteByte(messageSeparatorByte)
-
- switch v := data.(type) {
- case string:
- b.WriteString(messageTypeString.String())
- b.WriteByte(messageSeparatorByte)
- b.WriteString(v)
- case int:
- b.WriteString(messageTypeInt.String())
- b.WriteByte(messageSeparatorByte)
- binary.Write(b, binary.LittleEndian, v)
- case bool:
- b.WriteString(messageTypeBool.String())
- b.WriteByte(messageSeparatorByte)
- 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
- res, err := json.Marshal(data)
- if err != nil {
- ms.buf.Put(b)
- return nil, err
- }
- b.WriteString(messageTypeJSON.String())
- b.WriteByte(messageSeparatorByte)
- b.Write(res)
- }
-
- message := b.Bytes()
- ms.buf.Put(b)
-
- return message, nil
-}
-
-var errInvalidTypeMessage = errors.New("Type %s is invalid for message: %s")
-
-// 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 (ms *messageSerializer) deserialize(event []byte, websocketMessage []byte) (interface{}, error) {
- dataStartIdx := ms.prefixAndSepIdx + len(event) + 3
- if len(websocketMessage) <= dataStartIdx {
- return nil, errors.New("websocket invalid message: " + string(websocketMessage))
- }
-
- 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
-func (ms *messageSerializer) getWebsocketCustomEvent(websocketMessage []byte) []byte {
- if len(websocketMessage) < ms.prefixAndSepIdx {
- return nil
- }
- s := websocketMessage[ms.prefixAndSepIdx:]
- evt := s[:bytes.IndexByte(s, messageSeparatorByte)]
- return evt
-}
diff --git a/websocket/server.go b/websocket/server.go
deleted file mode 100644
index 6aaaccac..00000000
--- a/websocket/server.go
+++ /dev/null
@@ -1,395 +0,0 @@
-package websocket
-
-import (
- "bytes"
- "sync"
- "sync/atomic"
-
- "github.com/kataras/iris/context"
-
- "github.com/gorilla/websocket"
-)
-
-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)
-
- // Server is the websocket Server's implementation.
- //
- // It listens for websocket clients (either from the javascript client-side or from any websocket implementation).
- // See `OnConnection` , to register a single event which will handle all incoming connections and
- // the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint.
- //
- // To serve the builtin javascript client-side library look the `websocket.ClientHandler`.
- Server struct {
- 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
- 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
- mu sync.RWMutex // for rooms.
- onConnectionListeners []ConnectionFunc
- //connectionPool sync.Pool // sadly we can't make this because the websocket connection is live until is closed.
- upgrader websocket.Upgrader
- }
-)
-
-// New returns a new websocket Server based on a configuration.
-// See `OnConnection` , to register a single event which will handle all incoming connections and
-// the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint.
-//
-// To serve the builtin javascript client-side library look the `websocket.ClientHandler`.
-func New(cfg Config) *Server {
- cfg = cfg.Validate()
-
- s := &Server{
- config: cfg,
- ClientSource: bytes.Replace(ClientSource, []byte(DefaultEvtMessageKey), cfg.EvtMessagePrefix, -1),
- connections: sync.Map{}, // ready-to-use, this is not necessary.
- rooms: make(map[string][]string),
- onConnectionListeners: make([]ConnectionFunc, 0),
- upgrader: websocket.Upgrader{
- HandshakeTimeout: cfg.HandshakeTimeout,
- ReadBufferSize: cfg.ReadBufferSize,
- WriteBufferSize: cfg.WriteBufferSize,
- Error: cfg.Error,
- CheckOrigin: cfg.CheckOrigin,
- Subprotocols: cfg.Subprotocols,
- EnableCompression: cfg.EnableCompression,
- },
- }
-
- return s
-}
-
-// Handler builds the handler based on the configuration and returns it.
-// It should be called once per Server, its result should be passed
-// as a middleware to an iris route which will be responsible
-// to register the websocket's endpoint.
-//
-// Endpoint is the path which the websocket Server will listen for clients/connections.
-//
-// To serve the builtin javascript client-side library look the `websocket.ClientHandler`.
-func (s *Server) Handler() context.Handler {
- return func(ctx context.Context) {
- c := s.Upgrade(ctx)
- if c.Err() != nil {
- return
- }
- // NOTE TO ME: fire these first BEFORE startReader and startPinger
- // in order to set the events and any messages to send
- // the startPinger will send the OK to the client and only
- // then the client is able to send and receive from Server
- // when all things are ready and only then. DO NOT change this order.
-
- // fire the on connection event callbacks, if any
- for i := range s.onConnectionListeners {
- s.onConnectionListeners[i](c)
- }
-
- // start the ping and the messages reader
- c.Wait()
- }
-}
-
-// Upgrade upgrades the HTTP Server connection to the WebSocket protocol.
-//
-// The responseHeader is included in the response to the client's upgrade
-// request. Use the responseHeader to specify cookies (Set-Cookie) and the
-// application negotiated subprotocol (Sec--Protocol).
-//
-// If the upgrade fails, then Upgrade replies to the client with an HTTP error
-// response and the return `Connection.Err()` is filled with that error.
-//
-// For a more high-level function use the `Handler()` and `OnConnection` events.
-// This one does not starts the connection's writer and reader, so after your `On/OnMessage` events registration
-// the caller has to call the `Connection#Wait` function, otherwise the connection will be not handled.
-func (s *Server) Upgrade(ctx context.Context) Connection {
- conn, err := s.upgrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), ctx.ResponseWriter().Header())
- if err != nil {
- ctx.Application().Logger().Warnf("websocket error: %v\n", err)
- // ctx.StatusCode(503) // Status Service Unavailable
- return &connection{err: err}
- }
-
- return s.handleConnection(ctx, conn)
-}
-
-func (s *Server) addConnection(c *connection) {
- s.connections.Store(c.id, c)
-}
-
-func (s *Server) getConnection(connID string) (*connection, bool) {
- if cValue, ok := s.connections.Load(connID); ok {
- // this cast is not necessary,
- // we know that we always save a connection, but for good or worse let it be here.
- if conn, ok := cValue.(*connection); ok {
- return conn, ok
- }
- }
-
- return nil, false
-}
-
-// wrapConnection wraps an underline connection to an iris websocket connection.
-// It does NOT starts its writer, reader and event mux, the caller is responsible for that.
-func (s *Server) handleConnection(ctx context.Context, websocketConn *websocket.Conn) *connection {
- // use the config's id generator (or the default) to create a websocket client/connection id
- cid := s.config.IDGenerator(ctx)
- // create the new connection
- c := newServerConnection(ctx, s, websocketConn, cid)
- // add the connection to the Server's list
- s.addConnection(c)
-
- // join to itself
- s.Join(c.id, c.id)
-
- return c
-}
-
-// OnConnection is the main event you, as developer, will work with each of the websocket connections.
-func (s *Server) OnConnection(cb ConnectionFunc) {
- s.onConnectionListeners = append(s.onConnectionListeners, cb)
-}
-
-// IsConnected returns true if the connection with that ID is connected to the Server
-// useful when you have defined a custom connection id generator (based on a database)
-// and you want to check if that connection is already connected (on multiple tabs)
-func (s *Server) IsConnected(connID string) bool {
- _, found := s.getConnection(connID)
- return found
-}
-
-// Join joins a websocket client to a room,
-// first parameter is the room name and the second the connection.ID()
-//
-// You can use connection.Join("room name") instead.
-func (s *Server) Join(roomName string, connID string) {
- s.mu.Lock()
- s.join(roomName, connID)
- s.mu.Unlock()
-}
-
-// join used internally, no locks used.
-func (s *Server) join(roomName string, connID string) {
- if s.rooms[roomName] == nil {
- s.rooms[roomName] = make([]string, 0)
- }
- s.rooms[roomName] = append(s.rooms[roomName], connID)
-}
-
-// IsJoined reports if a specific room has a specific connection into its values.
-// First parameter is the room name, second is the connection's id.
-//
-// It returns true when the "connID" is joined to the "roomName".
-func (s *Server) IsJoined(roomName string, connID string) bool {
- s.mu.RLock()
- room := s.rooms[roomName]
- s.mu.RUnlock()
-
- if room == nil {
- return false
- }
-
- for _, connid := range room {
- if connID == connid {
- return true
- }
- }
-
- return false
-}
-
-// LeaveAll kicks out a connection from ALL of its joined rooms
-func (s *Server) LeaveAll(connID string) {
- s.mu.Lock()
- for name := range s.rooms {
- s.leave(name, connID)
- }
- s.mu.Unlock()
-}
-
-// Leave leaves a websocket client from a room,
-// first parameter is the room name and the second the connection.ID()
-//
-// You can use connection.Leave("room name") instead.
-// Returns true if the connection has actually left from the particular room.
-func (s *Server) Leave(roomName string, connID string) bool {
- s.mu.Lock()
- left := s.leave(roomName, connID)
- s.mu.Unlock()
- return left
-}
-
-// leave used internally, no locks used.
-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 {
- for i := range s.rooms[roomName] {
- if s.rooms[roomName][i] == connID {
- s.rooms[roomName] = append(s.rooms[roomName][:i], s.rooms[roomName][i+1:]...)
- left = true
- break
- }
- }
- if len(s.rooms[roomName]) == 0 { // if room is empty then delete it
- delete(s.rooms, roomName)
- }
- }
-
- if left {
- // fire the on room leave connection's listeners,
- // the existence check is not necessary here.
- if c, ok := s.getConnection(connID); ok {
- c.fireOnLeave(roomName)
- }
- }
- return
-}
-
-// GetTotalConnections returns the number of total connections.
-func (s *Server) GetTotalConnections() (n int) {
- s.connections.Range(func(k, v interface{}) bool {
- n++
- return true
- })
-
- return n
-}
-
-// GetConnections returns all connections.
-func (s *Server) GetConnections() (conns []Connection) {
- s.connections.Range(func(k, v interface{}) bool {
- conn, ok := v.(*connection)
- if !ok {
- // if for some reason (should never happen), the value is not stored as *connection
- // then stop the iteration and don't continue insertion of the result connections
- // in order to avoid any issues while end-dev will try to iterate a nil entry.
- return false
- }
- conns = append(conns, conn)
- return true
- })
-
- return
-}
-
-// GetConnection returns single connection
-func (s *Server) GetConnection(connID string) Connection {
- conn, ok := s.getConnection(connID)
- if !ok {
- return nil
- }
-
- return conn
-}
-
-// GetConnectionsByRoom returns a list of Connection
-// which are joined to this room.
-func (s *Server) GetConnectionsByRoom(roomName string) (conns []Connection) {
- if connIDs, found := s.rooms[roomName]; found {
- for _, connID := range connIDs {
- // existence check is not necessary here.
- if cValue, ok := s.connections.Load(connID); ok {
- if conn, ok := cValue.(*connection); ok {
- conns = append(conns, conn)
- }
- }
- }
- }
-
- return
-}
-
-// emitMessage is the main 'router' of the messages coming from the connection
-// this is the main function which writes the RAW websocket messages to the client.
-// It sends them(messages) to the correct room (self, broadcast or to specific client)
-//
-// You don't have to use this generic method, exists only for extreme
-// apps which you have an external goroutine with a list of custom connection list.
-//
-// You SHOULD use connection.EmitMessage/Emit/To().Emit/EmitMessage instead.
-// let's keep it unexported for the best.
-func (s *Server) emitMessage(from, to string, data []byte) {
- if to != All && to != Broadcast {
- s.mu.RLock()
- room := s.rooms[to]
- s.mu.RUnlock()
- if room != nil {
- // it suppose to send the message to a specific room/or a user inside its own room
- for _, connectionIDInsideRoom := range room {
- if c, ok := s.getConnection(connectionIDInsideRoom); ok {
- c.writeDefault(data) //send the message to the client(s)
- } else {
- // the connection is not connected but it's inside the room, we remove it on disconnect but for ANY CASE:
- cid := connectionIDInsideRoom
- if c != nil {
- cid = c.id
- }
- s.Leave(cid, to)
- }
- }
- }
- } else {
- // it suppose to send the message to all opened connections or to all except the sender.
- s.connections.Range(func(k, v interface{}) bool {
- connID, ok := k.(string)
- if !ok {
- // should never happen.
- return true
- }
-
- if to != All && to != connID { // if it's not suppose to send to all connections (including itself)
- if to == Broadcast && from == connID { // if broadcast to other connections except this
- // 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.
- return true
- }
-
- }
-
- // not necessary cast.
- conn, ok := v.(*connection)
- if ok {
- // send to the client(s) when the top validators passed
- conn.writeDefault(data)
- }
-
- return ok
- })
- }
-}
-
-// Disconnect force-disconnects a websocket connection based on its connection.ID()
-// What it does?
-// 1. remove the connection from the list
-// 2. leave from all joined rooms
-// 3. fire the disconnect callbacks, if any
-// 4. close the underline connection and return its error, if any.
-//
-// 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 conn, ok := s.getConnection(connID); ok {
- atomic.StoreUint32(&conn.disconnected, 1)
- // fire the disconnect callbacks, if any.
- conn.fireDisconnect()
- // close the underline connection and return its error, if any.
- err = conn.underline.Close()
-
- s.connections.Delete(connID)
- }
-
- return
-}
diff --git a/websocket/websocket.go b/websocket/websocket.go
index fff59634..88cc0199 100644
--- a/websocket/websocket.go
+++ b/websocket/websocket.go
@@ -1,69 +1,107 @@
-/*Package websocket provides rich websocket support for the iris web framework.
-
-Source code and other details for the project are available at GitHub:
-
- https://github.com/kataras/iris/tree/master/websocket
-
-Example code:
-
-
- package main
-
- import (
- "fmt"
-
- "github.com/kataras/iris"
- "github.com/kataras/iris/context"
-
- "github.com/kataras/iris/websocket"
- )
-
- func main() {
- app := iris.New()
-
- app.Get("/", func(ctx context.Context) {
- ctx.ServeFile("websockets.html", false)
- })
-
- setupWebsocket(app)
-
- // x2
- // http://localhost:8080
- // http://localhost:8080
- // write something, press submit, see the result.
- app.Run(iris.Addr(":8080"))
- }
-
- func setupWebsocket(app *iris.Application) {
- // create our echo websocket server
- ws := websocket.New(websocket.Config{
- ReadBufferSize: 1024,
- WriteBufferSize: 1024,
- })
- ws.OnConnection(handleConnection)
-
- // register the server's endpoint.
- // see the inline javascript code in the websockets.html,
- // this endpoint is used to connect to the server.
- app.Get("/echo", ws.Handler())
-
- // serve the javascript builtin client-side library,
- // see websockets.html script tags, this path is used.
- app.Any("/iris-ws.js", func(ctx context.Context) {
- ctx.Write(websocket.ClientSource)
- })
- }
-
- func handleConnection(c websocket.Connection) {
- // Read events from browser
- c.On("chat", func(msg string) {
- // Print the message to the console
- fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg)
- // Write message back to the client message owner:
- // c.Emit("chat", msg)
- c.To(websocket.Broadcast).Emit("chat", msg)
- })
- }
-
-*/
package websocket
+
+import (
+ "github.com/kataras/iris/context"
+
+ "github.com/kataras/neffos"
+ "github.com/kataras/neffos/gobwas"
+ "github.com/kataras/neffos/gorilla"
+)
+
+var (
+ // GorillaUpgrader is an upgrader type for the gorilla/websocket subprotocol implementation.
+ // Should be used on `New` to construct the websocket server.
+ GorillaUpgrader = gorilla.Upgrader
+ // GobwasUpgrader is an upgrader type for the gobwas/ws subprotocol implementation.
+ // Should be used on `New` to construct the websocket server.
+ GobwasUpgrader = gobwas.Upgrader
+ // DefaultGorillaUpgrader is a gorilla/websocket Upgrader with all fields set to the default values.
+ DefaultGorillaUpgrader = gorilla.DefaultUpgrader
+ // DefaultGobwasUpgrader is a gobwas/ws Upgrader with all fields set to the default values.
+ DefaultGobwasUpgrader = gobwas.DefaultUpgrader
+ // New constructs and returns a new websocket server.
+ // Listens to incoming connections automatically, no further action is required from the caller.
+ // The second parameter is the "connHandler", it can be
+ // filled as `Namespaces`, `Events` or `WithTimeout`, same namespaces and events can be used on the client-side as well,
+ // Use the `Conn#IsClient` on any event callback to determinate if it's a client-side connection or a server-side one.
+ //
+ // See examples for more.
+ New = neffos.New
+
+ // GorillaDialer is a gorilla/websocket dialer with all fields set to the default values.
+ GorillaDialer = gorilla.DefaultDialer
+ // GobwasDialer is a gobwas/ws dialer with all fields set to the default values.
+ GobwasDialer = gobwas.DefaultDialer
+ // Dial establishes a new websocket client connection.
+ // Context "ctx" is used for handshake timeout.
+ // Dialer "dial" can be either `GorillaDialer` or `GobwasDialer`,
+ // custom dialers can be used as well when complete the `Socket` and `Dialer` interfaces for valid client.
+ // URL "url" is the endpoint of the websocket server, i.e "ws://localhost:8080/echo".
+ // The last parameter, and the most important one is the "connHandler", it can be
+ // filled as `Namespaces`, `Events` or `WithTimeout`, same namespaces and events can be used on the server-side as well.
+ //
+ // See examples for more.
+ Dial = neffos.Dial
+
+ // OnNamespaceConnect is the event name which its callback is fired right before namespace connect,
+ // if non-nil error then the remote connection's `Conn.Connect` will fail and send that error text.
+ // Connection is not ready to emit data to the namespace.
+ OnNamespaceConnect = neffos.OnNamespaceConnect
+ // OnNamespaceConnected is the event name which its callback is fired after namespace successfully connected.
+ // Connection is ready to emit data back to the namespace.
+ OnNamespaceConnected = neffos.OnNamespaceConnected
+ // OnNamespaceDisconnect is the event name which its callback is fired when
+ // remote namespace disconnection or local namespace disconnection is happening.
+ // For server-side connections the reply matters, so if error returned then the client-side cannot disconnect yet,
+ // for client-side the return value does not matter.
+ OnNamespaceDisconnect = neffos.OnNamespaceDisconnect // if allowed to connect then it's allowed to disconnect as well.
+ // OnRoomJoin is the event name which its callback is fired right before room join.
+ OnRoomJoin = neffos.OnRoomJoin // able to check if allowed to join.
+ // OnRoomJoined is the event name which its callback is fired after the connection has successfully joined to a room.
+ OnRoomJoined = neffos.OnRoomJoined // able to broadcast messages to room.
+ // OnRoomLeave is the event name which its callback is fired right before room leave.
+ OnRoomLeave = neffos.OnRoomLeave // able to broadcast bye-bye messages to room.
+ // OnRoomLeft is the event name which its callback is fired after the connection has successfully left from a room.
+ OnRoomLeft = neffos.OnRoomLeft // if allowed to join to a room, then its allowed to leave from it.
+ // OnAnyEvent is the event name which its callback is fired when incoming message's event is not declared to the ConnHandler(`Events` or `Namespaces`).
+ OnAnyEvent = neffos.OnAnyEvent // when event no match.
+ // OnNativeMessage is fired on incoming native/raw websocket messages.
+ // If this event defined then an incoming message can pass the check (it's an invalid message format)
+ // with just the Message's Body filled, the Event is "OnNativeMessage" and IsNative always true.
+ // This event should be defined under an empty namespace in order this to work.
+ OnNativeMessage = neffos.OnNativeMessage
+
+ // IsSystemEvent reports whether the "event" is a system event,
+ // OnNamespaceConnect, OnNamespaceConnected, OnNamespaceDisconnect,
+ // OnRoomJoin, OnRoomJoined, OnRoomLeave and OnRoomLeft.
+ IsSystemEvent = neffos.IsSystemEvent
+ // Reply is a special type of custom error which sends a message back to the other side
+ // with the exact same incoming Message's Namespace (and Room if specified)
+ // except its body which would be the given "body".
+ Reply = neffos.Reply
+)
+
+// Handler returns an Iris handler to be served in a route of an Iris application.
+func Handler(s *neffos.Server) context.Handler {
+ return func(ctx context.Context) {
+ s.Upgrade(ctx.ResponseWriter(), ctx.Request(), func(socket neffos.Socket) neffos.Socket {
+ return &socketWrapper{
+ Socket: socket,
+ ctx: ctx,
+ }
+ })
+ }
+}
+
+type socketWrapper struct {
+ neffos.Socket
+ ctx context.Context
+}
+
+// GetContext returns the Iris Context from a websocket connection.
+func GetContext(c *neffos.Conn) context.Context {
+ if sw, ok := c.Socket().(*socketWrapper); ok {
+ return sw.ctx
+ }
+ return nil
+}
diff --git a/websocket/websocket_go19.go b/websocket/websocket_go19.go
new file mode 100644
index 00000000..34d4802c
--- /dev/null
+++ b/websocket/websocket_go19.go
@@ -0,0 +1,63 @@
+// +build go1.9
+
+package websocket
+
+import (
+ "github.com/kataras/neffos"
+)
+
+type (
+ // Conn describes the main websocket connection's functionality.
+ // Its `Connection` will return a new `NSConn` instance.
+ // Each connection can connect to one or more declared namespaces.
+ // Each `NSConn` can join to multiple rooms.
+ Conn = neffos.Conn
+ // NSConn describes a connected connection to a specific namespace,
+ // it emits with the `Message.Namespace` filled and it can join to multiple rooms.
+ // A single `Conn` can be connected to one or more namespaces,
+ // each connected namespace is described by this structure.
+ NSConn = neffos.NSConn
+ // Room describes a connected connection to a room,
+ // emits messages with the `Message.Room` filled to the specific room
+ // and `Message.Namespace` to the underline `NSConn`'s namespace.
+ Room = neffos.Room
+ // CloseError can be used to send and close a remote connection in the event callback's return statement.
+ CloseError = neffos.CloseError
+
+ // MessageHandlerFunc is the definition type of the events' callback.
+ // Its error can be written to the other side on specific events,
+ // i.e on `OnNamespaceConnect` it will abort a remote namespace connection.
+ // See examples for more.
+ MessageHandlerFunc = neffos.MessageHandlerFunc
+
+ // Events completes the `ConnHandler` interface.
+ // It is a map which its key is the event name
+ // and its value the event's callback.
+ //
+ // Events type completes the `ConnHandler` itself therefore,
+ // can be used as standalone value on the `New` and `Dial` functions
+ // to register events on empty namespace as well.
+ //
+ // See `Namespaces`, `New` and `Dial` too.
+ Events = neffos.Events
+ // Namespaces completes the `ConnHandler` interface.
+ // Can be used to register one or more namespaces on the `New` and `Dial` functions.
+ // The key is the namespace literal and the value is the `Events`,
+ // a map with event names and their callbacks.
+ //
+ // See `WithTimeout`, `New` and `Dial` too.
+ Namespaces = neffos.Namespaces
+ // WithTimeout completes the `ConnHandler` interface.
+ // Can be used to register namespaces and events or just events on an empty namespace
+ // with Read and Write timeouts.
+ //
+ // See `New` and `Dial`.
+ WithTimeout = neffos.WithTimeout
+ // The Message is the structure which describes the incoming and outcoming data.
+ // Emitter's "body" argument is the `Message.Body` field.
+ // Emitter's return non-nil error is the `Message.Err` field.
+ // If native message sent then the `Message.Body` is filled with the body and
+ // when incoming native message then the `Message.Event` is the `OnNativeMessage`,
+ // native messages are allowed only when an empty namespace("") and its `OnNativeMessage` callback are present.
+ Message = neffos.Message
+)