kataras / neffos

A modern, fast and scalable websocket framework with elegant API written in Go

Home Page:http://bit.ly/neffos-wiki

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[BUG]Binary data contains ";" will be droped!

StartAt24 opened this issue · comments

Sorry to bother you. But i have found the bug.
It is in neffos.js.

I'm using protobuf with neffos.js. The data that protobuf make counld contain ;. So that my message will be drop in neffos.js in here.

  var isArrayBuffer = data instanceof ArrayBuffer;
    var dts;

    console.log(isArrayBuffer)

    if (isArrayBuffer) {
        var arr = new Uint8Array(data);
        var sepCount = 1;
        var lastSepIndex = 0;
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] == messageSeparatorCharCode) { // sep char.
                sepCount++;
                lastSepIndex = i;
            }
        }
       // Drop here!!!!!!!!!!!!
        if (sepCount != validMessageSepCount) {
            msg.isInvalid = true;
            console.log("return at validMessageSepCount")
            return msg;
        }
        dts = splitN(textDecoder.decode(arr.slice(0, lastSepIndex)), messageSeparator, validMessageSepCount - 2);
        dts.push(data.slice(lastSepIndex + 1, data.length));
        msg.SetBinary = true;
    }
    else {
        dts = splitN(data, messageSeparator, validMessageSepCount - 1);
    }

I thank it is a bug. Cannot just split message by ;.

Here is the reproducible code.

Server

package main

import (
	"github.com/kataras/iris"
	"fmt"
	"github.com/kataras/iris/websocket"
	"github.com/kataras/neffos"
	"time"
)

// 全局变量
var page = struct {
    Title string
}{"Collector"}

func main(){
	app := iris.New()

	ws_server := startWebSocketServer(app)

	go pub_thread(ws_server)

	app.RegisterView(iris.HTML("./static", ".html"))
	app.Get("/" ,func(ctx iris.Context){
		ctx.ViewData("Page", page)
		ctx.View("index.html")
	})

	app.HandleDir("/", "./static")	
    app.Run(iris.Addr(":5000"), iris.WithoutPathCorrection)
}

var serverEvents = websocket.Namespaces{
	"default": websocket.Events{
		websocket.OnNamespaceConnected: func(nsConn *websocket.NSConn, msg websocket.Message) error {
			// with `websocket.GetContext` you can retrieve the Iris' `Context`.
			ctx := websocket.GetContext(nsConn.Conn)

			fmt.Printf("[%s] connected to namespace [%s] with IP [%s]\n",
				nsConn, msg.Namespace,
				ctx.RemoteAddr())
			return nil
		},
		websocket.OnNamespaceDisconnect: func(nsConn *websocket.NSConn, msg websocket.Message) error {
			fmt.Printf("[%s] disconnected from namespace [%s]\n", nsConn, msg.Namespace)
			return nil
		},
		"stream": func(nsConn *websocket.NSConn, msg websocket.Message) error {
			// room.String() returns -> NSConn.String() returns -> Conn.String() returns -> Conn.ID()
			fmt.Printf("[%s] sent: %s", nsConn, string(msg.Body))

			// Write message back to the client message owner with:
			// nsConn.Emit("chat", msg)
			// Write message to all except this client with:
			nsConn.Conn.Server().Broadcast(nsConn, msg)
			return nil
		},
	},
}

func startWebSocketServer(app *iris.Application) *neffos.Server{
	server := websocket.New(websocket.DefaultGorillaUpgrader, serverEvents)
	server.OnConnect = func(c *websocket.Conn) error {
		fmt.Printf("[%s] connected to the server.\n", c)
		
		return nil
	}

	server.OnDisconnect = func(c *websocket.Conn){
		fmt.Printf("[%s] disconnected from the server.", c)
	}

	fmt.Printf("Listening on: %d\nPress CTRL/CMD+C to interrupt.\n", 5000)

	idGen := func(ctx iris.Context) string {
		if username := ctx.GetHeader("X-Username"); username != "" {
			return username
		}

		return websocket.DefaultIDGenerator(ctx)
	}

	app.Get("/stream", websocket.Handler(server, idGen))

	return server
}

func pub_thread(serve *neffos.Server){
	png:= [...] byte{';',';',';',';',';'}
	slice := png[:]
	for{
		serve.Broadcast(nil, neffos.Message{SetBinary: true,  Body:slice, Namespace: "default"})
		time.Sleep(1*time.Second)
	}
}

Client

<html>
    <button> useless button</button>

    <script src="https://cdn.jsdelivr.net/npm/neffos.js@latest/dist/neffos.js"></script>
    <script>
    
        var scheme = document.location.protocol == "https:" ? "wss" : "ws";
        var port = document.location.port ? ":" + document.location.port : "";
        var wsURL = scheme + "://" + document.location.hostname + port + "/stream";
        
        function handleError(reason) {
            console.log(reason);
        }
        
        function handleNamespaceConnectedConn(nsConn) {

        }
        // const username = window.prompt("Your username?");
        async function runExample() {
            // You can omit the "default" and simply define only Events, the namespace will be an empty string"",
            // however if you decide to make any changes on this example make sure the changes are reflecting inside the ../server.go file as well.
            try {
                const conn = await neffos.dial(wsURL, {
                    default: { // "default" namespace.
                        _OnNamespaceConnected: function (nsConn, msg) {
                            handleNamespaceConnectedConn(nsConn)
                        },
                        _OnNamespaceDisconnect: function (nsConn, msg) {
                        },
                        stream: function (nsConn, msg) { // "stream" event.
                            console.log(msg.Body);
                            console.log(msg)
                        }
                    }
                },{
                    headers: {
                        "X-Username": "",
                    }
                });
                // You can either wait to conenct or just conn.connect("connect")
                // and put the `handleNamespaceConnectedConn` inside `_OnNamespaceConnected` callback instead.
                // const nsConn = await conn.connect("default");
                // nsConn.emit(...); handleNamespaceConnectedConn(nsConn);
                conn.connect("default");
            } catch (err) {
                handleError(err);
            }
        }

        runExample()
    </script>
</html>

Hello @StartAt24,

Does the protobuf example: https://github.com/kataras/neffos/blob/master/_examples/protobuf/main.go fits your needs? I see your point but messages containing ; are escaped when sending from javascript client (see neffos.js). So, could you please give me a reproducible example (preferable upload to a github repository) so I can run it and resolve in order to fix any issue?

Thanks,
Gerasimos Maropoulos

Hi @kataras , run the protobuf example. I have got some problems.
When i send message from ./protobuf client to server. The javascript client got this problem:

image

If i send message fron javascript client, the other javascript client will receive the message, but the ./protobuf client got nothing. And the ./protobuf server will print remote error: proto: can't skip unknown wire type 4. You can see it in the screenshot above.

What i want is : send binary data from 'go side' to 'javascript side'.

Hi @StartAt24, these issues were different. I fixed both of them. Message separator was not the problem. Please read:

Protobuf example updated to send data as binary with Conn.Write(Message{... SetBinary: true}).

Thanks for the bug report and sorry for the delay, I had to publish some other packages too.