mirror of
https://github.com/kataras/iris.git
synced 2026-01-08 12:31:58 +00:00
Add notes for the new lead maintainer of the open-source iris project and align with @get-ion/ion by @hiveminded
Former-commit-id: da4f38eb9034daa49446df3ee529423b98f9b331
This commit is contained in:
5
websocket/AUTHORS
Normal file
5
websocket/AUTHORS
Normal file
@@ -0,0 +1,5 @@
|
||||
# This is the official list of Iris Websocket authors for copyright
|
||||
# purposes.
|
||||
|
||||
Gerasimos Maropoulos <kataras2006@hotmail.com>
|
||||
Bill Qeras, Jr. <hiveminded@tutanota.com>
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
Copyright (c) 2017 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
|
||||
@@ -10,8 +10,8 @@ notice, this list of conditions and the following disclaimer.
|
||||
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 Gerasimos Maropoulos nor the name of his
|
||||
username, kataras, may be used to endorse or promote products derived from
|
||||
* 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
|
||||
@@ -24,29 +24,4 @@ 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.
|
||||
|
||||
Third-Parties:
|
||||
|
||||
Copyright (c) 2013 The Gorilla 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.
|
||||
|
||||
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 HOLDER 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.
|
||||
11
websocket/README.md
Normal file
11
websocket/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Websocket
|
||||
|
||||
Rich websocket support for the [iris](https://github.com/kataras/iris) web framework.
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Chat](_examples/chat/main.go)
|
||||
* [Native Messages](_examples/native-messages/main.go)
|
||||
* [Connection List](_examples/connectionlist/main.go)
|
||||
* [TLS Enabled](_examples/secure/main.go)
|
||||
* [Custom Raw Go Client](_examples/custom-go-client/main.go)
|
||||
56
websocket/_examples/chat/main.go
Normal file
56
websocket/_examples/chat/main.go
Normal file
@@ -0,0 +1,56 @@
|
||||
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) // second parameter: enable gzip?
|
||||
})
|
||||
|
||||
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 on an 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 built'n client-side library,
|
||||
// see weboskcets.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, c.Context() is the iris's http context.
|
||||
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)
|
||||
})
|
||||
}
|
||||
33
websocket/_examples/chat/websockets.html
Normal file
33
websocket/_examples/chat/websockets.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<input id="input" type="text" />
|
||||
<button onclick="send()">Send</button>
|
||||
<pre id="output"></pre>
|
||||
<script src="/iris-ws.js"></script>
|
||||
<script>
|
||||
var input = document.getElementById("input");
|
||||
var output = document.getElementById("output");
|
||||
|
||||
// Ws comes from the auto-served '/iris-ws.js'
|
||||
var socket = new Ws("ws://localhost:8080/echo");
|
||||
socket.OnConnect(function () {
|
||||
output.innerHTML += "Status: Connected\n";
|
||||
});
|
||||
|
||||
socket.OnDisconnect(function () {
|
||||
output.innerHTML += "Status: Disconnected\n";
|
||||
});
|
||||
|
||||
// read events from the server
|
||||
socket.On("chat", function (msg) {
|
||||
addMessage(msg)
|
||||
});
|
||||
|
||||
function send() {
|
||||
addMessage("Me: "+input.value) // write ourselves
|
||||
socket.Emit("chat", input.value);// send chat event data to the websocket server
|
||||
input.value = ""; // clear the input
|
||||
}
|
||||
|
||||
function addMessage(msg) {
|
||||
output.innerHTML += msg + "\n";
|
||||
}
|
||||
</script>
|
||||
98
websocket/_examples/connectionlist/main.go
Normal file
98
websocket/_examples/connectionlist/main.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/view"
|
||||
|
||||
"github.com/kataras/iris/websocket"
|
||||
)
|
||||
|
||||
type clientPage struct {
|
||||
Title string
|
||||
Host string
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.RegisterView(view.HTML("./templates", ".html")) // select the html engine to serve templates
|
||||
|
||||
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("/my_endpoint", ws.Handler())
|
||||
|
||||
// serve the javascript built'n client-side library,
|
||||
// see weboskcets.html script tags, this path is used.
|
||||
app.Any("/iris-ws.js", func(ctx context.Context) {
|
||||
ctx.Write(websocket.ClientSource)
|
||||
})
|
||||
|
||||
app.StaticWeb("/js", "./static/js") // serve our custom javascript code
|
||||
|
||||
app.Get("/", func(ctx context.Context) {
|
||||
ctx.ViewData("", clientPage{"Client Page", "localhost:8080"})
|
||||
ctx.View("client.html")
|
||||
})
|
||||
|
||||
Conn := make(map[websocket.Connection]bool)
|
||||
var myChatRoom = "room1"
|
||||
var mutex = new(sync.Mutex)
|
||||
|
||||
ws.OnConnection(func(c websocket.Connection) {
|
||||
c.Join(myChatRoom)
|
||||
mutex.Lock()
|
||||
Conn[c] = true
|
||||
mutex.Unlock()
|
||||
c.On("chat", func(message string) {
|
||||
if message == "leave" {
|
||||
c.Leave(myChatRoom)
|
||||
c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.")
|
||||
c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.")
|
||||
return
|
||||
}
|
||||
})
|
||||
c.OnDisconnect(func() {
|
||||
mutex.Lock()
|
||||
delete(Conn, c)
|
||||
mutex.Unlock()
|
||||
fmt.Printf("\nConnection with ID: %s has been disconnected!\n", c.ID())
|
||||
})
|
||||
})
|
||||
|
||||
var delay = 1 * time.Second
|
||||
go func() {
|
||||
i := 0
|
||||
for {
|
||||
mutex.Lock()
|
||||
broadcast(Conn, fmt.Sprintf("aaaa %d\n", i))
|
||||
mutex.Unlock()
|
||||
time.Sleep(delay)
|
||||
i++
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
i := 0
|
||||
for {
|
||||
mutex.Lock()
|
||||
broadcast(Conn, fmt.Sprintf("aaaa2 %d\n", i))
|
||||
mutex.Unlock()
|
||||
time.Sleep(delay)
|
||||
i++
|
||||
}
|
||||
}()
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
||||
func broadcast(Conn map[websocket.Connection]bool, message string) {
|
||||
for k := range Conn {
|
||||
k.To("room1").Emit("chat", message)
|
||||
}
|
||||
}
|
||||
38
websocket/_examples/connectionlist/static/js/chat.js
Normal file
38
websocket/_examples/connectionlist/static/js/chat.js
Normal file
@@ -0,0 +1,38 @@
|
||||
var messageTxt;
|
||||
var messages;
|
||||
|
||||
$(function () {
|
||||
|
||||
messageTxt = $("#messageTxt");
|
||||
messages = $("#messages");
|
||||
|
||||
|
||||
w = new Ws("ws://" + HOST + "/my_endpoint");
|
||||
w.OnConnect(function () {
|
||||
console.log("Websocket connection established");
|
||||
});
|
||||
|
||||
w.OnDisconnect(function () {
|
||||
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
|
||||
});
|
||||
|
||||
w.On("chat", function (message) {
|
||||
appendMessage($("<div>" + message + "</div>"));
|
||||
});
|
||||
|
||||
$("#sendBtn").click(function () {
|
||||
w.Emit("chat", messageTxt.val().toString());
|
||||
messageTxt.val("");
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
|
||||
function appendMessage(messageDiv) {
|
||||
var theDiv = messages[0];
|
||||
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
|
||||
messageDiv.appendTo(messages);
|
||||
if (doScroll) {
|
||||
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
|
||||
}
|
||||
}
|
||||
24
websocket/_examples/connectionlist/templates/client.html
Normal file
24
websocket/_examples/connectionlist/templates/client.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{ .Title}}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="messages"
|
||||
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
|
||||
|
||||
</div>
|
||||
<input type="text" id="messageTxt" />
|
||||
<button type="button" id="sendBtn">Send</button>
|
||||
<script type="text/javascript">
|
||||
var HOST = {{.Host}}
|
||||
</script>
|
||||
<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
|
||||
<!-- This is auto-serving by the iris, you don't need to have this file in your disk-->
|
||||
<script src="/iris-ws.js" type="text/javascript"></script>
|
||||
<!-- -->
|
||||
<script src="js/chat.js" type="text/javascript"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
177
websocket/_examples/custom-go-client/main.go
Normal file
177
websocket/_examples/custom-go-client/main.go
Normal file
@@ -0,0 +1,177 @@
|
||||
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
|
||||
|
||||
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/tree/master/adaptors/websocket/message.go , client.go and client.js
|
||||
// to understand the buffer line:
|
||||
buffer := []byte(fmt.Sprintf("iris-websocket-message:%v;0;%v;%v;", 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!")
|
||||
|
||||
}
|
||||
63
websocket/_examples/native-messages/main.go
Normal file
63
websocket/_examples/native-messages/main.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/view"
|
||||
|
||||
"github.com/kataras/iris/websocket"
|
||||
)
|
||||
|
||||
/* Native messages no need to import the iris-ws.js to the ./templates.client.html
|
||||
Use of: OnMessage and EmitMessage.
|
||||
|
||||
|
||||
NOTICE: IF YOU HAVE RAN THE PREVIOUS EXAMPLES YOU HAVE TO CLEAR YOUR BROWSER's CACHE
|
||||
BECAUSE chat.js is different than the CACHED. OTHERWISE YOU WILL GET Ws is undefined from the browser's console, because it will use the cached.
|
||||
*/
|
||||
|
||||
type clientPage struct {
|
||||
Title string
|
||||
Host string
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
app.RegisterView(view.HTML("./templates", ".html")) // select the html engine to serve templates
|
||||
|
||||
ws := websocket.New(websocket.Config{
|
||||
// to enable binary messages (useful for protobuf):
|
||||
// BinaryMessages: true,
|
||||
})
|
||||
|
||||
// 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("/my_endpoint", ws.Handler())
|
||||
|
||||
app.StaticWeb("/js", "./static/js") // serve our custom javascript code
|
||||
|
||||
app.Get("/", func(ctx context.Context) {
|
||||
ctx.ViewData("", clientPage{"Client Page", "localhost:8080"})
|
||||
ctx.View("client.html")
|
||||
})
|
||||
|
||||
ws.OnConnection(func(c websocket.Connection) {
|
||||
|
||||
c.OnMessage(func(data []byte) {
|
||||
message := string(data)
|
||||
c.To(websocket.Broadcast).EmitMessage([]byte("Message from: " + c.ID() + "-> " + message)) // broadcast to all clients except this
|
||||
c.EmitMessage([]byte("Me: " + message)) // writes to itself
|
||||
})
|
||||
|
||||
c.OnDisconnect(func() {
|
||||
fmt.Printf("\nConnection with ID: %s has been disconnected!", c.ID())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
||||
}
|
||||
38
websocket/_examples/native-messages/static/js/chat.js
Normal file
38
websocket/_examples/native-messages/static/js/chat.js
Normal file
@@ -0,0 +1,38 @@
|
||||
var messageTxt;
|
||||
var messages;
|
||||
|
||||
$(function () {
|
||||
|
||||
messageTxt = $("#messageTxt");
|
||||
messages = $("#messages");
|
||||
|
||||
|
||||
w = new WebSocket("ws://" + HOST + "/my_endpoint");
|
||||
w.onopen = function () {
|
||||
console.log("Websocket connection enstablished");
|
||||
};
|
||||
|
||||
w.onclose = function () {
|
||||
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
|
||||
};
|
||||
w.onmessage = function(message){
|
||||
appendMessage($("<div>" + message.data + "</div>"));
|
||||
};
|
||||
|
||||
|
||||
$("#sendBtn").click(function () {
|
||||
w.send(messageTxt.val().toString());
|
||||
messageTxt.val("");
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
|
||||
function appendMessage(messageDiv) {
|
||||
var theDiv = messages[0];
|
||||
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
|
||||
messageDiv.appendTo(messages);
|
||||
if (doScroll) {
|
||||
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
|
||||
}
|
||||
}
|
||||
21
websocket/_examples/native-messages/templates/client.html
Normal file
21
websocket/_examples/native-messages/templates/client.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{ .Title}}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="messages"
|
||||
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
|
||||
|
||||
</div>
|
||||
<input type="text" id="messageTxt" />
|
||||
<button type="button" id="sendBtn">Send</button>
|
||||
<script type="text/javascript">
|
||||
var HOST = {{.Host}}
|
||||
</script>
|
||||
<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
|
||||
<script src="js/chat.js" type="text/javascript"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
176
websocket/_examples/secure/main.go
Normal file
176
websocket/_examples/secure/main.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/view"
|
||||
|
||||
"github.com/kataras/iris/websocket"
|
||||
)
|
||||
|
||||
type clientPage struct {
|
||||
Title string
|
||||
Host string
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.RegisterView(view.HTML("./templates", ".html")) // select the html engine to serve templates
|
||||
|
||||
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("/my_endpoint", ws.Handler())
|
||||
|
||||
// serve the javascript built'n client-side library,
|
||||
// see weboskcets.html script tags, this path is used.
|
||||
app.Any("/iris-ws.js", func(ctx context.Context) {
|
||||
ctx.Write(websocket.ClientSource)
|
||||
})
|
||||
|
||||
app.StaticWeb("/js", "./static/js")
|
||||
app.Get("/", func(ctx context.Context) {
|
||||
// send our custom javascript source file before client really asks for that
|
||||
// using the go v1.8's HTTP/2 Push.
|
||||
// Note that you have to listen using ListenTLS in order this to work.
|
||||
if err := ctx.ResponseWriter().Push("/js/chat.js", nil); err != nil {
|
||||
ctx.Application().Logger().Warnln(err.Error())
|
||||
}
|
||||
ctx.ViewData("", clientPage{"Client Page", ctx.Host()})
|
||||
ctx.View("client.html")
|
||||
})
|
||||
|
||||
var myChatRoom = "room1"
|
||||
|
||||
ws.OnConnection(func(c websocket.Connection) {
|
||||
// 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.
|
||||
|
||||
// ctx := c.Context()
|
||||
|
||||
// join to a room (optional)
|
||||
c.Join(myChatRoom)
|
||||
|
||||
c.On("chat", func(message string) {
|
||||
if message == "leave" {
|
||||
c.Leave(myChatRoom)
|
||||
c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.")
|
||||
c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.")
|
||||
return
|
||||
}
|
||||
// to all except this connection ->
|
||||
// c.To(websocket.Broadcast).Emit("chat", "Message from: "+c.ID()+"-> "+message)
|
||||
// to all connected clients: c.To(websocket.All)
|
||||
|
||||
// to the client itself ->
|
||||
//c.Emit("chat", "Message from myself: "+message)
|
||||
|
||||
//send the message to the whole room,
|
||||
//all connections are inside this room will receive this message
|
||||
c.To(myChatRoom).Emit("chat", "From: "+c.ID()+": "+message)
|
||||
})
|
||||
|
||||
// or create a new leave event
|
||||
// c.On("leave", func() {
|
||||
// c.Leave(myChatRoom)
|
||||
// })
|
||||
|
||||
c.OnDisconnect(func() {
|
||||
fmt.Printf("Connection with ID: %s has been disconnected!\n", c.ID())
|
||||
})
|
||||
})
|
||||
|
||||
listenTLS(app)
|
||||
|
||||
}
|
||||
|
||||
// a test listenTLS for our localhost
|
||||
func listenTLS(app *iris.Application) {
|
||||
|
||||
const (
|
||||
testTLSCert = `-----BEGIN CERTIFICATE-----
|
||||
MIIDBTCCAe2gAwIBAgIJAOYzROngkH6NMA0GCSqGSIb3DQEBBQUAMBkxFzAVBgNV
|
||||
BAMMDmxvY2FsaG9zdDo4MDgwMB4XDTE3MDIxNzAzNDM1NFoXDTI3MDIxNTAzNDM1
|
||||
NFowGTEXMBUGA1UEAwwObG9jYWxob3N0OjgwODAwggEiMA0GCSqGSIb3DQEBAQUA
|
||||
A4IBDwAwggEKAoIBAQCfsiVHO14FpKsi0pvBv68oApQm2MO+dCvq87sDU4E0QJhG
|
||||
KV1RCUmQVypChEqdLlUQsopcXSyKwbWoyg1/KNHYO3DHMfePb4bC1UD2HENq7Ph2
|
||||
8QJTEi/CJvUB9hqke/YCoWYdjFiI3h3Hw8q5whGO5XR3R23z69vr5XxoNlcF2R+O
|
||||
TdkzArd0CWTZS27vbgdnyi9v3Waydh/rl+QRtPUgEoCEqOOkMSMldXO6Z9GlUk9b
|
||||
FQHwIuEnlSoVFB5ot5cqebEjJnWMLLP83KOCQekJeHZOyjeTe8W0Fy1DGu5fvFNh
|
||||
xde9e/7XlFE//00vT7nBmJAUV/2CXC8U5lsjLEqdAgMBAAGjUDBOMB0GA1UdDgQW
|
||||
BBQOfENuLn/t0Z4ZY1+RPWaz7RBH+TAfBgNVHSMEGDAWgBQOfENuLn/t0Z4ZY1+R
|
||||
PWaz7RBH+TAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBG7AEEuIq6
|
||||
rWCE5I2t4IXz0jN7MilqEhUWDbUajl1paYf6Ikx5QhMsFx21p6WEWYIYcnWAKZe2
|
||||
chAgnnGojuxdx0qjiaH4N4xWGHsWhaesnIF1xJepLlX3kJZQURvRxM4wlljlQPIb
|
||||
9tqzKP131K1HDqplAtp7nWQ72m3J0ZfzH0mYIUxuaS/uQIVtgKqdilwy/VE5dRZ9
|
||||
QFIb4G9TnNThXMqgTLjfNr33jVbTuv6fzKHYNbCkP3L10ydEs/ddlREmtsn9nE8Q
|
||||
XCTIYXzA2kr5kWk7d3LkUiSvu3g2S1Ol1YaIKaOQyRveseCGwR4xohLT+dPUW9dL
|
||||
3hDVLlwE3mB3
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
`
|
||||
testTLSKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAn7IlRzteBaSrItKbwb+vKAKUJtjDvnQr6vO7A1OBNECYRild
|
||||
UQlJkFcqQoRKnS5VELKKXF0sisG1qMoNfyjR2DtwxzH3j2+GwtVA9hxDauz4dvEC
|
||||
UxIvwib1AfYapHv2AqFmHYxYiN4dx8PKucIRjuV0d0dt8+vb6+V8aDZXBdkfjk3Z
|
||||
MwK3dAlk2Utu724HZ8ovb91msnYf65fkEbT1IBKAhKjjpDEjJXVzumfRpVJPWxUB
|
||||
8CLhJ5UqFRQeaLeXKnmxIyZ1jCyz/NyjgkHpCXh2Tso3k3vFtBctQxruX7xTYcXX
|
||||
vXv+15RRP/9NL0+5wZiQFFf9glwvFOZbIyxKnQIDAQABAoIBAEzBx4ExW8PCni8i
|
||||
o5LAm2PTuXniflMwa1uGwsCahmOjGI3AnAWzPRSPkNRf2a0q8+AOsMosTphy+umi
|
||||
FFKmQBZ6m35i2earaE6FSbABbbYbKGGi/ccH2sSrDOBgdfXRTzF8eiSBrJw8hnvZ
|
||||
87rNOLtCNnSOdJ7lItODfgRo+fLo4uQenJ8VONYwtwm1ejn8qLXq8O5zF66IYUD6
|
||||
gAzqOiAWumgZL0tEmndeQ+noe4STpJZlOjiCsA12NiJaKDDeDIn5A/pXce+bYNfJ
|
||||
k4yoroyq/JXBkhyuZDvX9vYp5AA+Q68h8/KmsKkifUgSGSHun5/80lYyT/f60TLX
|
||||
PxT9GYECgYEA0s8qck7L29nBBTQ6IPF3GHGmqiRdfH+qhP/Jn4NtoW3XuVe4A15i
|
||||
REq1L8WAiOUIBnBaD8HzbeioqJJYx1pu7x9h/GCNDhdBfwhTjnBe+JjfLqvJKnc0
|
||||
HUT5wj4DVqattxKzUW8kTRBSWtVremzeffDo+EL6dnR7Bc02Ibs4WpUCgYEAwe34
|
||||
Uqhie+/EFr4HjYRUNZSNgYNAJkKHVxk4qGzG5VhvjPafnHUbo+Kk/0QW7eIB+kvR
|
||||
FDO8oKh9wTBrWZEcLJP4jDIKh4y8hZTo9B8EjxFONXVxZlOSYuGjheL8AiLzE7L9
|
||||
C1spaKMM/MyxAXDRHpG/NeEgXM7Kn6kUGwJdNekCgYAshLNiEGHcu8+XWcAs1NFh
|
||||
yB56L9PORuerzpi1pvuv65JzAaNKktQNt/krbXoHbtaTBYb/bOYLf+aeMsmsz9w9
|
||||
g1MeCQXAxAiA2zFKE1D7Ds2S/ZQt8559z+MusgnicrCcyMY1nFL+M0QxCoD4CaWy
|
||||
0v1f8EUUXuTcBMo5tV/hQQKBgDoBBW8jsiFDu7DZscSgOde00QZVzZAkAfsJLisi
|
||||
LfNXGjZdZawUUuoX1iYLpZgNK25D0wtp1hdvjf2Ej/dAMd8bexHjvcaBT7ncqjiq
|
||||
NmDcWjofIIXspTIyLwjStXGmJnJT7N/CqoYDjtTmHGND7Shpi3mAFn/r0isjFUJm
|
||||
2J5RAoGALuGXxzmSRWmkIp11F/Qr3PBFWBWkrRWaH2TRLMhrU/wO8kCsSyo4PmAZ
|
||||
ltOfD7InpDiCu43hcDPQ/29FUbDnmAhvMnmIQuHXGgPF/LhqEhbKPA/o/eZdQVCK
|
||||
QG+tmveBBIYMed5YbWstZu/95lIHF+u8Hl+Z6xgveozfE5yqiUA=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
||||
`
|
||||
)
|
||||
|
||||
// create the key and cert files on the fly, and delete them when this test finished
|
||||
certFile, ferr := ioutil.TempFile("", "cert")
|
||||
|
||||
if ferr != nil {
|
||||
panic(ferr)
|
||||
}
|
||||
|
||||
keyFile, ferr := ioutil.TempFile("", "key")
|
||||
if ferr != nil {
|
||||
panic(ferr)
|
||||
}
|
||||
|
||||
certFile.WriteString(testTLSCert)
|
||||
keyFile.WriteString(testTLSKey)
|
||||
|
||||
// https://localhost
|
||||
app.ListenTLS("localhost:443", certFile.Name(), keyFile.Name())
|
||||
|
||||
certFile.Close()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
os.Remove(certFile.Name())
|
||||
|
||||
keyFile.Close()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
os.Remove(keyFile.Name())
|
||||
}
|
||||
38
websocket/_examples/secure/static/js/chat.js
Normal file
38
websocket/_examples/secure/static/js/chat.js
Normal file
@@ -0,0 +1,38 @@
|
||||
var messageTxt;
|
||||
var messages;
|
||||
|
||||
$(function () {
|
||||
|
||||
messageTxt = $("#messageTxt");
|
||||
messages = $("#messages");
|
||||
|
||||
/* secure wss because we ListenTLS */
|
||||
w = new Ws("wss://" + HOST + "/my_endpoint");
|
||||
w.OnConnect(function () {
|
||||
console.log("Websocket connection established");
|
||||
});
|
||||
|
||||
w.OnDisconnect(function () {
|
||||
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
|
||||
});
|
||||
|
||||
w.On("chat", function (message) {
|
||||
appendMessage($("<div>" + message + "</div>"));
|
||||
});
|
||||
|
||||
$("#sendBtn").click(function () {
|
||||
w.Emit("chat", messageTxt.val().toString());
|
||||
messageTxt.val("");
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
|
||||
function appendMessage(messageDiv) {
|
||||
var theDiv = messages[0];
|
||||
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
|
||||
messageDiv.appendTo(messages);
|
||||
if (doScroll) {
|
||||
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
|
||||
}
|
||||
}
|
||||
24
websocket/_examples/secure/templates/client.html
Normal file
24
websocket/_examples/secure/templates/client.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{ .Title}}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="messages"
|
||||
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
|
||||
|
||||
</div>
|
||||
<input type="text" id="messageTxt" />
|
||||
<button type="button" id="sendBtn">Send</button>
|
||||
<script type="text/javascript">
|
||||
var HOST = {{.Host}}
|
||||
</script>
|
||||
<script src="/js/vendor/jquery-2.2.3.min.js"></script>
|
||||
<!-- This is auto-serving by the iris, you don't need to have this file in your disk-->
|
||||
<script src="/iris-ws.js"></script>
|
||||
<!-- -->
|
||||
<script src="/js/chat.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,20 +1,29 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------
|
||||
// ----------------Client side websocket javascript source which is typescript compiled
|
||||
// ------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------
|
||||
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.StaticCacheDuration.
|
||||
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;
|
||||
// bytes is missing here for reasons I will explain somewhen
|
||||
var websocketJSONMessageType = 4;
|
||||
var websocketMessagePrefix = "iris-websocket-message:";
|
||||
var websocketMessageSeparator = ";";
|
||||
@@ -102,7 +111,7 @@ var Ws = (function () {
|
||||
return this._msg(event, t, m);
|
||||
};
|
||||
Ws.prototype.decodeMessage = function (event, websocketMessage) {
|
||||
//q-websocket-message;user;4;themarshaledstringfromajsonstruct
|
||||
//iris-websocket-message;user;4;themarshaledstringfromajsonstruct
|
||||
var skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
|
||||
if (websocketMessage.length < skipLen + 1) {
|
||||
return null;
|
||||
@@ -145,7 +154,7 @@ var Ws = (function () {
|
||||
// if native message then calls the fireNativeMessage
|
||||
// else calls the fireMessage
|
||||
//
|
||||
// remember q gives you the freedom of native websocket messages if you don't want to use this client side at all.
|
||||
// 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;
|
||||
@@ -213,7 +222,7 @@ var Ws = (function () {
|
||||
Ws.prototype.EmitMessage = function (websocketMessage) {
|
||||
this.conn.send(websocketMessage);
|
||||
};
|
||||
// Emit sends an q-custom websocket message
|
||||
// Emit sends an iris-custom websocket message
|
||||
Ws.prototype.Emit = function (event, data) {
|
||||
var messageStr = this.encodeMessage(event, data);
|
||||
this.EmitMessage(messageStr);
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// ----------------Client side websocket commented typescript source code --------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
// export to client.go:clientSource []byte
|
||||
// export to client.go:ClientSource []byte
|
||||
|
||||
const websocketStringMessageType = 0;
|
||||
const websocketIntMessageType = 1;
|
||||
@@ -124,7 +114,7 @@ class Ws {
|
||||
}
|
||||
|
||||
private decodeMessage<T>(event: string, websocketMessage: string): T | any {
|
||||
//q-websocket-message;user;4;themarshaledstringfromajsonstruct
|
||||
//iris-websocket-message;user;4;themarshaledstringfromajsonstruct
|
||||
let skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
|
||||
if (websocketMessage.length < skipLen + 1) {
|
||||
return null;
|
||||
@@ -169,7 +159,7 @@ class Ws {
|
||||
// if native message then calls the fireNativeMessage
|
||||
// else calls the fireMessage
|
||||
//
|
||||
// remember q gives you the freedom of native websocket messages if you don't want to use this client side at all.
|
||||
// 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 = <string>evt.data;
|
||||
@@ -252,7 +242,7 @@ class Ws {
|
||||
this.conn.send(websocketMessage);
|
||||
}
|
||||
|
||||
// Emit sends an q-custom websocket message
|
||||
// Emit sends an iris-custom websocket message
|
||||
Emit(event: string, data: any): void {
|
||||
let messageStr = this.encodeMessage(event, data);
|
||||
this.EmitMessage(messageStr);
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
@@ -40,15 +36,16 @@ var (
|
||||
// Config the websocket server configuration
|
||||
// all of these are optional.
|
||||
type Config struct {
|
||||
// Endpoint is the path which the websocket server will listen for clients/connections
|
||||
// Default value is empty string, if you don't set it the Websocket server is disabled.
|
||||
Endpoint string
|
||||
// ClientSourcePath is is the path which the client-side
|
||||
// will be auto-served when the server adapted to an Iris station.
|
||||
// Default value is "/iris-ws.js"
|
||||
ClientSourcePath string
|
||||
Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
|
||||
CheckOrigin func(r *http.Request) bool
|
||||
// IDGenerator used to create (and later on, set)
|
||||
// an ID for each incoming websocket connections (clients).
|
||||
// The request is an argument which you can use to generate the ID (from headers for example).
|
||||
// If empty then the ID is generated by DefaultIDGenerator: randomString(64)
|
||||
IDGenerator func(ctx context.Context) string
|
||||
|
||||
Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
|
||||
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
|
||||
@@ -76,11 +73,11 @@ type Config struct {
|
||||
// WriteBufferSize is the buffer size for the underline writer
|
||||
// Default value is 4096
|
||||
WriteBufferSize int
|
||||
// IDGenerator used to create (and later on, set)
|
||||
// an ID for each incoming websocket connections (clients).
|
||||
// The request is an argument which you can use to generate the ID (from headers for example).
|
||||
// If empty then the ID is generated by DefaultIDGenerator: randomString(64)
|
||||
IDGenerator func(ctx context.Context) string
|
||||
// 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.
|
||||
EnableCompression bool
|
||||
|
||||
// Subprotocols specifies the server's supported protocols in order of
|
||||
// preference. If this field is set, then the Upgrade method negotiates a
|
||||
@@ -91,11 +88,6 @@ type Config struct {
|
||||
|
||||
// Validate validates the configuration
|
||||
func (c Config) Validate() Config {
|
||||
|
||||
if c.ClientSourcePath == "" {
|
||||
c.ClientSourcePath = DefaultClientSourcePath
|
||||
}
|
||||
|
||||
// 0 means no timeout.
|
||||
if c.WriteTimeout < 0 {
|
||||
c.WriteTimeout = DefaultWebsocketWriteTimeout
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
@@ -142,6 +138,12 @@ type (
|
||||
// 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
|
||||
@@ -150,12 +152,12 @@ type (
|
||||
|
||||
// OnDisconnect registers a callback which fires when this connection is closed by an error or manual
|
||||
OnDisconnect(DisconnectFunc)
|
||||
// OnStatusCode registers a callback which fires when this connection occurs an error
|
||||
OnStatusCode(ErrorFunc)
|
||||
// OnError registers a callback which fires when this connection occurs an error
|
||||
OnError(ErrorFunc)
|
||||
// FireStatusCode can be used to send a custom error message to the connection
|
||||
//
|
||||
// It does nothing more than firing the OnStatusCode listeners. It doesn't sends anything to the client.
|
||||
FireStatusCode(errorMessage string)
|
||||
// It does nothing more than firing the OnError listeners. It doesn't sends anything to the client.
|
||||
FireOnError(errorMessage string)
|
||||
// To defines where server should send a message
|
||||
// returns an emitter to send messages
|
||||
To(string) Emitter
|
||||
@@ -209,7 +211,7 @@ type (
|
||||
// access to the Context, use with causion, you can't use response writer as you imagine.
|
||||
ctx context.Context
|
||||
values ConnectionValues
|
||||
server *server
|
||||
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
|
||||
@@ -221,7 +223,7 @@ type (
|
||||
|
||||
var _ Connection = &connection{}
|
||||
|
||||
func newConnection(ctx context.Context, s *server, underlineConn UnderlineConnection, id string) *connection {
|
||||
func newConnection(ctx context.Context, s *Server, underlineConn UnderlineConnection, id string) *connection {
|
||||
c := &connection{
|
||||
underline: underlineConn,
|
||||
id: id,
|
||||
@@ -334,7 +336,7 @@ func (c *connection) startReader() {
|
||||
_, data, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
c.FireStatusCode(err.Error())
|
||||
c.FireOnError(err.Error())
|
||||
}
|
||||
break
|
||||
} else {
|
||||
@@ -397,6 +399,10 @@ 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
|
||||
}
|
||||
@@ -415,11 +421,11 @@ func (c *connection) OnDisconnect(cb DisconnectFunc) {
|
||||
c.onDisconnectListeners = append(c.onDisconnectListeners, cb)
|
||||
}
|
||||
|
||||
func (c *connection) OnStatusCode(cb ErrorFunc) {
|
||||
func (c *connection) OnError(cb ErrorFunc) {
|
||||
c.onErrorListeners = append(c.onErrorListeners, cb)
|
||||
}
|
||||
|
||||
func (c *connection) FireStatusCode(errorMessage string) {
|
||||
func (c *connection) FireOnError(errorMessage string) {
|
||||
for _, cb := range c.onErrorListeners {
|
||||
cb(errorMessage)
|
||||
}
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// --------------------------------Emitter implementation-------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
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 = ";gowebsocket;to;all;except;me;"
|
||||
Broadcast = ";ionwebsocket;to;all;except;me;"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
@@ -15,16 +11,6 @@ import (
|
||||
"github.com/valyala/bytebufferpool"
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -----------------websocket messages and de/serialization implementation--------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
serializer, [de]websocketMessageSerialize the messages from the client to the websocketServer and from the websocketServer to the client
|
||||
*/
|
||||
|
||||
// The same values are exists on client side also
|
||||
const (
|
||||
websocketStringMessageType websocketMessageType = iota
|
||||
|
||||
@@ -1,83 +1,13 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// Server is the websocket server,
|
||||
// listens on the config's port, the critical part is the event OnConnection
|
||||
type Server interface {
|
||||
// Attach adapts the websocket server to an Iris instance.
|
||||
// see websocket.go
|
||||
Attach(app *iris.Application)
|
||||
|
||||
// Handler returns the iris.Handler
|
||||
// which is setted to the 'Websocket Endpoint path',
|
||||
// the client should target to this handler's developer's custom path
|
||||
// ex: app.Any("/myendpoint", mywebsocket.Handler())
|
||||
Handler() context.Handler
|
||||
|
||||
// OnConnection this is the main event you, as developer, will work with each of the websocket connections
|
||||
OnConnection(cb ConnectionFunc)
|
||||
|
||||
/*
|
||||
connection actions, same as the connection's method,
|
||||
but these methods accept the connection ID,
|
||||
which is useful when the developer maps
|
||||
this id with a database field (using config.IDGenerator).
|
||||
*/
|
||||
|
||||
// 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)
|
||||
IsConnected(connID string) bool
|
||||
|
||||
// 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.
|
||||
Join(roomName string, connID string)
|
||||
|
||||
// LeaveAll kicks out a connection from ALL of its joined rooms
|
||||
LeaveAll(connID string)
|
||||
|
||||
// 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.
|
||||
Leave(roomName string, connID string) bool
|
||||
|
||||
// GetConnectionsByRoom returns a list of Connection
|
||||
// are joined to this room.
|
||||
GetConnectionsByRoom(roomName string) []Connection
|
||||
|
||||
// Disconnect force-disconnects a websocket connection
|
||||
// based on its connection.ID()
|
||||
// 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.
|
||||
Disconnect(connID string) error
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// --------------------------------Connection key-based list----------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
type connectionKV struct {
|
||||
key string // the connection ID
|
||||
value *connection
|
||||
@@ -147,57 +77,79 @@ func (cs *connections) remove(key string) (*connection, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// --------------------------------Server implementation--------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
type (
|
||||
// ConnectionFunc is the callback which fires when a client/connection is connected to the server.
|
||||
// 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)
|
||||
|
||||
// websocketRoomPayload is used as payload from the connection to the server
|
||||
// websocketRoomPayload is used as payload from the connection to the Server
|
||||
websocketRoomPayload struct {
|
||||
roomName string
|
||||
connectionID string
|
||||
}
|
||||
|
||||
// payloads, connection -> server
|
||||
// payloads, connection -> Server
|
||||
websocketMessagePayload struct {
|
||||
from string
|
||||
to string
|
||||
data []byte
|
||||
}
|
||||
|
||||
server struct {
|
||||
// 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 built'n javascript client-side library look the `websocket.ClientHandler`.
|
||||
Server struct {
|
||||
config Config
|
||||
connections connections
|
||||
rooms map[string][]string // by default a connection is joined to a room which has the connection id as its name
|
||||
mu sync.Mutex // for rooms
|
||||
onConnectionListeners []ConnectionFunc
|
||||
//connectionPool *sync.Pool // sadly I can't make this because the websocket connection is live until is closed.
|
||||
//connectionPool sync.Pool // sadly we can't make this because the websocket connection is live until is closed.
|
||||
handler context.Handler
|
||||
}
|
||||
)
|
||||
|
||||
var _ Server = &server{}
|
||||
// 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 built'n javascript client-side library look the `websocket.ClientHandler`.
|
||||
func New(cfg Config) *Server {
|
||||
return &Server{
|
||||
config: cfg.Validate(),
|
||||
rooms: make(map[string][]string, 0),
|
||||
onConnectionListeners: make([]ConnectionFunc, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// server implementation
|
||||
|
||||
func (s *server) Handler() context.Handler {
|
||||
// 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 built'n javascript client-side library look the `websocket.ClientHandler`.
|
||||
func (s *Server) Handler() context.Handler {
|
||||
// build the upgrader once
|
||||
c := s.config
|
||||
|
||||
upgrader := websocket.Upgrader{
|
||||
ReadBufferSize: c.ReadBufferSize,
|
||||
WriteBufferSize: c.WriteBufferSize,
|
||||
Error: c.Error,
|
||||
CheckOrigin: c.CheckOrigin,
|
||||
Subprotocols: c.Subprotocols,
|
||||
HandshakeTimeout: c.HandshakeTimeout,
|
||||
ReadBufferSize: c.ReadBufferSize,
|
||||
WriteBufferSize: c.WriteBufferSize,
|
||||
Error: c.Error,
|
||||
CheckOrigin: c.CheckOrigin,
|
||||
Subprotocols: c.Subprotocols,
|
||||
EnableCompression: c.EnableCompression,
|
||||
}
|
||||
|
||||
return func(ctx context.Context) {
|
||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||
// 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
|
||||
@@ -207,8 +159,8 @@ func (s *server) Handler() context.Handler {
|
||||
// response.
|
||||
conn, err := upgrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), ctx.ResponseWriter().Header())
|
||||
if err != nil {
|
||||
ctx.Application().Log("websocket error: %v", err)
|
||||
ctx.StatusCode(iris.StatusServiceUnavailable)
|
||||
ctx.Application().Logger().Warnf("websocket error: %v\n", err)
|
||||
ctx.StatusCode(503) // Status Service Unavailable
|
||||
return
|
||||
}
|
||||
s.handleConnection(ctx, conn)
|
||||
@@ -216,12 +168,12 @@ func (s *server) Handler() context.Handler {
|
||||
}
|
||||
|
||||
// handleConnection creates & starts to listening to a new connection
|
||||
func (s *server) handleConnection(ctx context.Context, websocketConn UnderlineConnection) {
|
||||
func (s *Server) handleConnection(ctx context.Context, websocketConn UnderlineConnection) {
|
||||
// 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 := newConnection(ctx, s, websocketConn, cid)
|
||||
// add the connection to the server's list
|
||||
// add the connection to the Server's list
|
||||
s.connections.add(cid, c)
|
||||
|
||||
// join to itself
|
||||
@@ -230,7 +182,7 @@ func (s *server) handleConnection(ctx context.Context, websocketConn UnderlineCo
|
||||
// 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
|
||||
// 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
|
||||
@@ -251,25 +203,35 @@ func (s *server) handleConnection(ctx context.Context, websocketConn UnderlineCo
|
||||
his/her websocket connections without even use the connection itself.
|
||||
|
||||
Another question may be:
|
||||
Q: Why you use server as the main actioner for all of the connection actions?
|
||||
For example the server.Disconnect(connID) manages the connection internal fields, is this code-style correct?
|
||||
A: It's the correct code-style for these type of applications and libraries, server manages all, the connnection's functions
|
||||
should just do some internal checks (if needed) and push the action to its parent, which is the server, the server is able to
|
||||
Q: Why you use Server as the main actioner for all of the connection actions?
|
||||
For example the Server.Disconnect(connID) manages the connection internal fields, is this code-style correct?
|
||||
A: It's the correct code-style for these type of applications and libraries, Server manages all, the connnection's functions
|
||||
should just do some internal checks (if needed) and push the action to its parent, which is the Server, the Server is able to
|
||||
remove a connection, the rooms of its connected and all these things, so in order to not split the logic, we have the main logic
|
||||
here, in the server, and let the connection with some exported functions whose exists for the per-connection action user's code-style.
|
||||
here, in the Server, and let the connection with some exported functions whose exists for the per-connection action user's code-style.
|
||||
|
||||
Ok my english are s** I can feel it, but these comments are mostly for me.
|
||||
*/
|
||||
|
||||
// OnConnection this is the main event you, as developer, will work with each of the websocket connections
|
||||
func (s *server) OnConnection(cb ConnectionFunc) {
|
||||
/*
|
||||
connection actions, same as the connection's method,
|
||||
but these methods accept the connection ID,
|
||||
which is useful when the developer maps
|
||||
this id with a database field (using config.IDGenerator).
|
||||
*/
|
||||
|
||||
// OnConnection is the main event you, as developer, will work with each of the websocket connections.
|
||||
func (s *Server) OnConnection(cb ConnectionFunc) {
|
||||
if s.handler == nil {
|
||||
|
||||
}
|
||||
s.onConnectionListeners = append(s.onConnectionListeners, cb)
|
||||
}
|
||||
|
||||
// IsConnected returns true if the connection with that ID is connected to the server
|
||||
// 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 {
|
||||
func (s *Server) IsConnected(connID string) bool {
|
||||
c := s.connections.get(connID)
|
||||
return c != nil
|
||||
}
|
||||
@@ -278,14 +240,14 @@ func (s *server) IsConnected(connID string) bool {
|
||||
// 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) {
|
||||
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) {
|
||||
func (s *Server) join(roomName string, connID string) {
|
||||
if s.rooms[roomName] == nil {
|
||||
s.rooms[roomName] = make([]string, 0)
|
||||
}
|
||||
@@ -293,7 +255,7 @@ func (s *server) join(roomName string, connID string) {
|
||||
}
|
||||
|
||||
// LeaveAll kicks out a connection from ALL of its joined rooms
|
||||
func (s *server) LeaveAll(connID string) {
|
||||
func (s *Server) LeaveAll(connID string) {
|
||||
s.mu.Lock()
|
||||
for name, connectionIDs := range s.rooms {
|
||||
for i := range connectionIDs {
|
||||
@@ -314,7 +276,7 @@ func (s *server) LeaveAll(connID string) {
|
||||
//
|
||||
// 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 {
|
||||
func (s *Server) Leave(roomName string, connID string) bool {
|
||||
s.mu.Lock()
|
||||
left := s.leave(roomName, connID)
|
||||
s.mu.Unlock()
|
||||
@@ -322,7 +284,7 @@ func (s *server) Leave(roomName string, connID string) bool {
|
||||
}
|
||||
|
||||
// leave used internally, no locks used.
|
||||
func (s *server) leave(roomName string, connID string) (left bool) {
|
||||
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 {
|
||||
@@ -348,7 +310,7 @@ func (s *server) leave(roomName string, connID string) (left bool) {
|
||||
|
||||
// GetConnectionsByRoom returns a list of Connection
|
||||
// which are joined to this room.
|
||||
func (s *server) GetConnectionsByRoom(roomName string) []Connection {
|
||||
func (s *Server) GetConnectionsByRoom(roomName string) []Connection {
|
||||
s.mu.Lock()
|
||||
var conns []Connection
|
||||
if connIDs, found := s.rooms[roomName]; found {
|
||||
@@ -370,7 +332,7 @@ func (s *server) GetConnectionsByRoom(roomName string) []Connection {
|
||||
//
|
||||
// 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) {
|
||||
func (s *Server) emitMessage(from, to string, data []byte) {
|
||||
if to != All && to != Broadcast && s.rooms[to] != nil {
|
||||
// it suppose to send the message to a specific room/or a user inside its own room
|
||||
for _, connectionIDInsideRoom := range s.rooms[to] {
|
||||
@@ -410,7 +372,7 @@ func (s *server) emitMessage(from, to string, data []byte) {
|
||||
// 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) {
|
||||
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)
|
||||
|
||||
@@ -1,63 +1,74 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
/*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
|
||||
|
||||
Installation
|
||||
|
||||
$ go get -u github.com/kataras/iris/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 built'n client-side library,
|
||||
// see weboskcets.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 (
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
// New returns a new websocket server policy adaptor.
|
||||
func New(cfg Config) Server {
|
||||
return &server{
|
||||
config: cfg.Validate(),
|
||||
rooms: make(map[string][]string, 0),
|
||||
onConnectionListeners: make([]ConnectionFunc, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func fixPath(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
if s[0] != '/' {
|
||||
s = "/" + s
|
||||
}
|
||||
|
||||
s = strings.Replace(s, "//", "/", -1)
|
||||
return s
|
||||
}
|
||||
|
||||
// Attach adapts the websocket server to one or more Iris instances.
|
||||
func (s *server) Attach(app *iris.Application) {
|
||||
wsPath := fixPath(s.config.Endpoint)
|
||||
if wsPath == "" {
|
||||
app.Log("websocket's configuration field 'Endpoint' cannot be empty, websocket server stops")
|
||||
return
|
||||
}
|
||||
|
||||
wsClientSidePath := fixPath(s.config.ClientSourcePath)
|
||||
if wsClientSidePath == "" {
|
||||
app.Log("websocket's configuration field 'ClientSourcePath' cannot be empty, websocket server stops")
|
||||
return
|
||||
}
|
||||
|
||||
// set the routing for client-side source (javascript) (optional)
|
||||
clientSideLookupName := "iris-websocket-client-side"
|
||||
wsHandler := s.Handler()
|
||||
app.Get(wsPath, wsHandler)
|
||||
// check if client side doesn't already exists
|
||||
if app.GetRoute(clientSideLookupName) == nil {
|
||||
// serve the client side on domain:port/iris-ws.js
|
||||
r, err := app.StaticContent(wsClientSidePath, "application/javascript", ClientSource)
|
||||
if err != nil {
|
||||
app.Log("websocket's route for javascript client-side library failed with: %v", err)
|
||||
return
|
||||
}
|
||||
r.Name = clientSideLookupName
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user