Code Monkey home page Code Monkey logo

neffos's Introduction

neffos chat example

build status report card view examples chat frontend pkg

About neffos

Neffos is a cross-platform real-time framework with expressive, elegant API written in Go. Neffos takes the pain out of development by easing common tasks used in real-time backend and frontend applications such as:

  • Scale-out using redis or nats*
  • Adaptive request upgradation and server dialing
  • Acknowledgements
  • Namespaces
  • Rooms
  • Broadcast
  • Event-Driven architecture
  • Request-Response architecture
  • Error Awareness
  • Asynchronous Broadcast
  • Timeouts
  • Encoding
  • Reconnection
  • Modern neffos API client for Browsers, Nodejs* and Go

Learning neffos

Quick View

Server

import (
    // [...]
    "github.com/kataras/neffos"
    "github.com/kataras/neffos/gorilla"
)

func runServer() {
    events := make(neffos.Namespaces)
    events.On("/v1", "workday", func(ns *neffos.NSConn, msg neffos.Message) error {
        date := string(msg.Body)

        t, err := time.Parse("01-02-2006", date)
        if err != nil {
            if n := ns.Conn.Increment("tries"); n >= 3 && n%3 == 0 {
                // Return custom error text to the client.
                return fmt.Errorf("Why not try this one? 06-24-2019")
            } else if n >= 6 && n%2 == 0 {
                // Fire the "notify" client event.
                ns.Emit("notify", []byte("What are you doing?"))
            }
            // Return the parse error back to the client.
            return err
        }

        weekday := t.Weekday()

        if weekday == time.Saturday || weekday == time.Sunday {
            return neffos.Reply([]byte("day off"))
        }

        // Reply back to the client.
        responseText := fmt.Sprintf("it's %s, do your job.", weekday)
        return neffos.Reply([]byte(responseText))
    })

    websocketServer := neffos.New(gorilla.DefaultUpgrader, events)

    // Fire the "/v1:notify" event to all clients after server's 1 minute.
    time.AfterFunc(1*time.Minute, func() {
        websocketServer.Broadcast(nil, neffos.Message{
            Namespace: "/v1",
            Event:     "notify",
            Body:      []byte("server is up and running for 1 minute"),
        })
    })

    router := http.NewServeMux()
    router.Handle("/", websocketServer)

    log.Println("Serving websockets on localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", router))
}

Go Client

func runClient() {
    ctx := context.TODO()
    events := make(neffos.Namespaces)
    events.On("/v1", "notify", func(c *neffos.NSConn, msg neffos.Message) error {
        log.Printf("Server says: %s\n", string(msg.Body))
        return nil
    })

    // Connect to the server.
    client, err := neffos.Dial(ctx,
        gorilla.DefaultDialer,
        "ws://localhost:8080",
        events)
    if err != nil {
        panic(err)
    }

    // Connect to a namespace.
    c, err := client.Connect(ctx, "/v1")
    if err != nil {
        panic(err)
    }

    fmt.Println("Please specify a date of format: mm-dd-yyyy")

    for {
        fmt.Print(">> ")
        var date string
        fmt.Scanf("%s", &date)

        // Send to the server and wait reply to this message.
        response, err := c.Ask(ctx, "workday", []byte(date))
        if err != nil {
            if neffos.IsCloseError(err) {
                // Check if the error is a close signal,
                // or make use of the `<- client.NotifyClose`
                // read-only channel instead.
                break
            }

            // >> 13-29-2019
            // error received: parsing time "13-29-2019": month out of range
            fmt.Printf("error received: %v\n", err)
            continue
        }

        // >> 06-29-2019
        // it's a day off!
        //
        // >> 06-24-2019
        // it's Monday, do your job.
        fmt.Println(string(response.Body))
    }
}

Javascript Client

Navigate to: https://github.com/kataras/neffos.js

Neffos contains extensive and thorough wiki making it easy to get started with the framework.

For a more detailed technical documentation you can head over to our godocs. And for executable code you can always visit the _examples repository's subdirectory.

Do you like to read while traveling?

You can download a PDF version of the E-Book today and be participated in the development of neffos.

https://iris-go.com/images/neffos-book-overview.png

Contributing

We'd love to see your contribution to the neffos real-time framework! For more information about contributing to the neffos project please check the CONTRIBUTING.md file.

  • neffos-contrib github organisation for more programming languages support, please invite yourself.

Security Vulnerabilities

If you discover a security vulnerability within neffos, please send an e-mail to [email protected]. All security vulnerabilities will be promptly addressed.

License

The word "neffos" has a greek origin and it is translated to "cloud" in English dictionary.

This project is licensed under the MIT license.

neffos's People

Contributors

dependabot[bot] avatar heliosgo avatar karoloslykos avatar kataras avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

neffos's Issues

[BUG] redis.NewStackExchange got radis err: NOAUTH Authentication required.

I can confirm that the configuration is correct and the same configuration can connect to the cluster using go-redis.
@kataras can you add a go-redis dependency?

Locate where the error occurred:

cluster, err := radix.NewCluster(cfg.Clusters)
if err != nil {
// maybe an
// ERR This instance has cluster support disabled
return nil, err
}

ref: https://github.com/mediocregopher/radix/blob/edd71ccab1b4dadde5b6e422e7af086230c0cf54/cluster.go#L203-L208

In V12 version, neffos websocket cannot set cross domain using gorilla library, and will prompt 403 error

Describe the bug
In V12 version, neffos websocket cannot set cross domain using gorilla library, and will prompt 403 error

My local solution is as follows:
// DefaultUpgrader is a gorilla/websocket Upgrader with all fields set to the default values.
var DefaultUpgrader = Upgrader(gorilla.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
})

to Reproduce
My suggestion is that you should configure “gorilla. Upgrade {}” through the developer user

Native Message Broadcasting with Scale out is not working

Hi all,

I try to use scaling out function with native messages,
but it doesn't work out.

First of all, I tried to use Conn.Server().Broadcast with scaling out. However, it was not working, then I tried Conn.Server().Do func instead.
Clients connect to the same ws server are able to receive messages while using Do func, but clients connect to different servers are not.

Is there any way that I can use scaling out function with native message mode?

Glad to hear your advice.
Thank you very mush for your help.

package main

import (
	"log"

	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/websocket"
	"github.com/kataras/neffos/stackexchange/nats"
)

type clientPage struct {
	Title string
	Host  string
}

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

	app.RegisterView(iris.HTML("./templates", ".html")) // select the html engine to serve templates

	// Almost all features of neffos are disabled because no custom message can pass
	// when app expects to accept and send only raw websocket native messages.
	// When only allow native messages is a fact?
	// When the registered namespace is just one and it's empty
	// and contains only one registered event which is the `OnNativeMessage`.
	// When `Events{...}` is used instead of `Namespaces{ "namespaceName": Events{...}}`
	// then the namespace is empty "".
	ws := websocket.New(websocket.DefaultGorillaUpgrader, websocket.Events{
		websocket.OnNativeMessage: func(nsConn *websocket.NSConn, msg websocket.Message) error {
			log.Printf("Server got: %s from [%s]", msg.Body, nsConn.Conn.ID())

			nsConn.Conn.Server().Broadcast(nsConn, msg)
			return nil
		},
	})

	exc, err := nats.NewStackExchange("nats://nats-streaming:4222")
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	if err != nil {
		panic(err)
	}
	ws.UseStackExchange(exc)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

	ws.OnConnect = func(c *websocket.Conn) error {
		log.Printf("[%s] Connected to server!", c.ID())
		return nil
	}

	ws.OnDisconnect = func(c *websocket.Conn) {
		log.Printf("[%s] Disconnected from server", c.ID())
	}

	app.HandleDir("/js", "./static/js") // serve our custom javascript code.

	// 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", websocket.Handler(ws))

	app.Get("/", func(ctx iris.Context) {
		ctx.View("client.html", clientPage{"Client Page", "localhost:80"})
	})

	// Target some browser windows/tabs to http://localhost:8080 and send some messages,
	// see the static/js/chat.js,
	// note that the client is using only the browser's native WebSocket API instead of the neffos one.
	app.Run(iris.Addr(":80"))
}

about websocket's room

hello
I want to implement a function with websocket.
the details are as follows:
i want to create a room and the room will return me an id.
When someone connects to service using websocket,he can via id to find the room and connect it.
How do I implement it?....

Do we need to manually handle the room leaving event?

Hello, @kataras , I am using the room function provided by neffos, and In my case I am not sure whether the client-side can call the Leave() method correctly, which may cause the room to not be properly recycled? Can the server-side manually disconnect the specified client connection to the room?

Rooms storage variable:

neffos/conn_namespace.go

Lines 22 to 25 in 15e3a17

// Dynamically channels/rooms for each connected namespace.
// Client can ask to join, server can forcely join a connection to a room.
// Namespace(room(fire event)).
rooms map[string]*Room

RoomJoin:

neffos/conn_namespace.go

Lines 216 to 218 in 15e3a17

ns.roomsMutex.Lock()
ns.rooms[msg.Room] = newRoom(ns, msg.Room)
ns.roomsMutex.Unlock()

RoomLeave:

neffos/conn_namespace.go

Lines 305 to 307 in 15e3a17

ns.roomsMutex.Lock()
delete(ns.rooms, msg.Room)
ns.roomsMutex.Unlock()

IDGenerator does not work

It is always return default id.

	events := make(websocket.Namespaces)
	events["willnet"]=controller.Willnet()

	websocketServer := websocket.New(websocket.DefaultGobwasUpgrader, events)

	websocketServer.IDGenerator = func(w http.ResponseWriter, r *http.Request) string {
		log.Println(111111)
		jwtStr:=r.Header.Get("jwt")
		token:=new(jwt.Token)
		if jwtStr == ""{
			return websocket.DefaultIDGenerator(nil)
		}
		err:=json.Unmarshal([]byte(jwtStr),token)
		if err!=nil{
			return websocket.DefaultIDGenerator(nil)
		}
		user := token.Claims.(jwt.MapClaims)
		return user["account"].(string)
	}

......

	websocketRouter := app.Get("/websocket/echo", websocket.Handler(websocketServer))

	//jwt
	jwtHandler := jwt.New(jwt.Config{
		Extractor:jwt.FromAuthHeader,
		ValidationKeyGetter: func(token *jwt.Token) (i interface{}, e error) {
			return []byte(os.Getenv("JWT_SECRET")),nil
		},
		SigningMethod:jwt.SigningMethodHS256,
	})

	websocketRouter.Use(jwtHandler.Serve)

When the client joined room, the server-side does not print '111111'.

[BUG] the neffos server hang up whole goroutines

Describe the bug
After upgrade lastest version, it still happened on productions. Debugging by dlv tool, I found the neffos block whole process.

Screenshots
4011585729190_ pic_hd
4021585729215_ pic_hd

Desktop (please complete the following information):

  • OS: Centos
  • Version 7.7

Could you have time to handle this questions?

Problem with CORS and websockets

Describe the bug
I have an issue with cors, when i run it the example in 2 diferents ports (3000, 3001) and i try to conect a new socket from 3001 to 3000 then the console throw the next error:
WebSocket connection to 'ws://localhost:3001/echo?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU' failed: Error during WebSocket handshake: Unexpected response code: 403.

To Reproduce
Use the example server.go on the route: _examples/websocket/basic/

package main

import (
	"log"

	"github.com/kataras/iris"
	"github.com/kataras/iris/websocket"

	// Used when "enableJWT" constant is true:
	"github.com/iris-contrib/middleware/jwt"
)

// values should match with the client sides as well.
const enableJWT = true
const namespace = "default"

// if namespace is empty then simply websocket.Events{...} can be used instead.
var serverEvents = websocket.Namespaces{
	namespace: 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)

			log.Printf("[%s] connected to namespace [%s] with IP [%s]",
				nsConn, msg.Namespace,
				ctx.RemoteAddr())
			return nil
		},
		websocket.OnNamespaceDisconnect: func(nsConn *websocket.NSConn, msg websocket.Message) error {
			log.Printf("[%s] disconnected from namespace [%s]", nsConn, msg.Namespace)
			return nil
		},
		"chat": func(nsConn *websocket.NSConn, msg websocket.Message) error {
			// room.String() returns -> NSConn.String() returns -> Conn.String() returns -> Conn.ID()
			log.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 main() {
	app := iris.New()
	websocketServer := websocket.New(
		websocket.DefaultGorillaUpgrader, /* DefaultGobwasUpgrader can be used too. */
		serverEvents)

	j := jwt.New(jwt.Config{
		// Extract by the "token" url,
		// so the client should dial with ws://localhost:8080/echo?token=$token
		Extractor: jwt.FromParameter("token"),

		ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
			return []byte("My Secret"), nil
		},

		// When set, the middleware verifies that tokens are signed
		// with the specific signing algorithm
		// If the signing method is not constant the
		// `Config.ValidationKeyGetter` callback field can be used
		// to implement additional checks
		// Important to avoid security issues described here:
		// https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
		SigningMethod: jwt.SigningMethodHS256,
	})

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

		return websocket.DefaultIDGenerator(ctx)
	}

	// serves the endpoint of ws://localhost:8080/echo
	// with optional custom ID generator.
	websocketRoute := app.Get("/echo", websocket.Handler(websocketServer, idGen))

	if enableJWT {
		// Register the jwt middleware (on handshake):
		websocketRoute.Use(j.Serve)
		// OR
		//
		// Check for token through the jwt middleware
		// on websocket connection or on any event:
		/* websocketServer.OnConnect = func(c *websocket.Conn) error {
		ctx := websocket.GetContext(c)
		if err := j.CheckJWT(ctx); err != nil {
			// will send the above error on the client
			// and will not allow it to connect to the websocket server at all.
			return err
		}

		user := ctx.Values().Get("jwt").(*jwt.Token)
		// or just: user := j.Get(ctx)

		log.Printf("This is an authenticated request\n")
		log.Printf("Claim content:")
		log.Printf("%#+v\n", user.Claims)

		log.Printf("[%s] connected to the server", c.ID())

		return nil
		} */
	}

	// serves the browser-based websocket client.
	app.Get("/", func(ctx iris.Context) {
		ctx.ServeFile("./browser/index.html", false)
	})

	// serves the npm browser websocket client usage example.
	app.HandleDir("/browserify", "./browserify")

	app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
}

Expected behavior
I would like to make petitions from the 2 servers

Posible fix
To catch this i made a middleware function wich delete the origin header (but this will be a rellay bad practice), code above:

        app.Use(func(ctx iris.Context) {
		ctx.Request().Header.Del("Origin")
		ctx.Next()
	})

[Question\Websocket] How to send binary data with neffos(websocket)?

#387 I have read this issue. But cannot found, websocket.Config

Then i found that, Iris websocket library is now merged with the neffos real-time framework
But i cann't find a correct way to send binary data with neffos.

Is there any config for neffos to send binary instead of string?

Does it support system push?

two problems:
1、How the system pushes announcements to every online user?
2、How to regularly promote announcements ?

[BUG] Reply to ask cron

Hi @kataras i try to replicate the example of Ask function between 2 go api but server side never catch the response from Reply function in client side.

[FEATURE REQUEST] adapters support for scalability

Like socketio or signalr, they provide serval adapters such as redis.
Hope neffos could provide the adapter interface. let users could add their own implemention;
And It would be great for builtin support serval great pubsub system like redis and nats

Thanks again for the great library.

Dart client?

Hello,

This is more of a question: is there a dart/flutter client for this library? I couldn't find one, and there seems to be nothing about it here in the issues as well.

Thanks,
Howe

[FEATURE REQUEST]Whether the authentication information can be provided with the message#Body when client joining the room?

I want to build a private room.
The client can join the private room but it must complete the authentication.
So I need to send authentication information to the server to determine whether the client's request to join is illegal.
But I find that nsConn.JoinRoom(ctx, "room") does not provide a way to send a custom message.
the following function also can't provide Message#Body filed:
neffos.OnRoomJoined: func(c *neffos.NSConn, msg neffos.Message) error
The client seems to be able to forcibly join any specific room.it bypass my certification.
I am a newcomer if I miss some useful API?

[INFO] How to integrate with Echo framework

Hi @kataras, I started to use neffos nearly weak ago and i really like it, where i work we decide to move into real time aplication, and i searched for a good socket package, i already used iris and i really like how it works, i was working with websockets of iris and when i updated iris i see some errors (i was in trubles before because updates of other libraries we use), then i read the documentation and i've changed all.

I also integrate the sockets with echo framework and i wold like to leave here how i made it for anyone who need it and also i think it will be good for this greate library if they have examples of implementatios with other to.

Then here is the code:

package main

import (
	"github.com/labstack/echo"
        "github.com/kataras/neffos"
	"github.com/kataras/neffos/gorilla"
	"github.com/labstack/echo"
)

type serverConn struct {
	*neffos.NSConn
}

func main() {
        router := echo.New()

        controller := new(serverConn)
        events := neffos.NewStruct(controller).
		SetNamespace("default").
		SetEventMatcher(neffos.EventTrimPrefixMatcher("On"))

        websocketServer := neffos.New(
		gorilla.DefaultUpgrader, /* DefaultGobwasUpgrader can be used too. */
		events)

       // Wrap the web socket handler with echo function to be compatible, both use http library to work
       router.Any("/echo", echo.WrapHandler(websocketServer), deleteOrigin)

        if err := router.Start(":8080"); err != nil {
		log.Fatalf("error in ListenAndServe: %s", err)
	}
}

// deleteOrigin allows others origins than local access to sockets
// if we avoid this handler then we can't connect from servers in other ports or routes
// NOTE: This isn't the best way to do it, but i can't found another way to do it.
func deleteOrigin(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		c.Request().Header.Del("Origin")
		return next(c)
	}
}

func (c *serverConn) OnNamespaceConnected(msg neffos.Message) error {
	log.Printf("[%s] connected to namespace [%s]",
		c.Conn, msg.Namespace)
	return nil
}

func (c *serverConn) OnNamespaceDisconnect(msg neffos.Message) error {
	log.Printf("[%s] disconnected from namespace [%s]", c.Conn, msg.Namespace)
	return nil
}

func (c *serverConn) OnChat(msg neffos.Message) error {
	log.Printf("[%s] sent: %s", c.Conn, string(msg.Body))
	c.Conn.Server().Broadcast(c.Conn, msg)
	return nil
}

This is only for the server file, if anyone want to test it use with the files in the route: _examples/basic/browser

One las thing: I don't know how to acces to the context of the request.

[FEATURE REQUEST] Rooms Authentication

The client-side JoinRoom method cannot pass metadata to the server-side for authenticating.
for now, the rooms are accessible to all users, but in some situations, we need only allow specific users to join some rooms.

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

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>

How to modify the format of websocket response

hi, the default websocket response is string, and messageSeparator is ;

;default;;chat;0;0;4

how can i modify, overwrite to json format

{
"Namespace": "default",
"Room" : "room1",
"Event": "chat",
"Body": "xxxxxx"
}

thx!

fatal error: concurrent map iteration and map write

fatal error: concurrent map iteration and map write

goroutine 46 [running]:
runtime.throw(0x1842c28, 0x26)
/usr/local/Cellar/go/1.14.4/libexec/src/runtime/panic.go:1116 +0x72 fp=0xc002fd9bf8 sp=0xc002fd9bc8 pc=0x433a62
runtime.mapiternext(0xc002fd9cd0)
/usr/local/Cellar/go/1.14.4/libexec/src/runtime/map.go:853 +0x552 fp=0xc002fd9c78 sp=0xc002fd9bf8 pc=0x40f5e2
runtime.mapiterinit(0x1603200, 0xc000e9bf80, 0xc002fd9cd0)
/usr/local/Cellar/go/1.14.4/libexec/src/runtime/map.go:843 +0x1c4 fp=0xc002fd9c98 sp=0xc002fd9c78 pc=0x40ef94
github.com/kataras/neffos.(*Server).GetConnections(0xc0008c6700, 0x0)
/Users/near/go/pkg/mod/github.com/kataras/[email protected]/server.go:606 +0x9b fp=0xc002fd9d40 sp=0xc002fd9c98 pc=0x10b8b6b

Hello kataras, how does neffos send messages from one websocket server to another, just like the code

	app := iris.Default()

	event := neffos.Namespaces{
		"web": neffos.Events{
			neffos.OnNamespaceConnected: func(nsConn *neffos.NSConn, msg neffos.Message) error {
				log.Printf("connected to namespace: %s", msg.Namespace)
				return nil
			},
			neffos.OnNamespaceDisconnect: func(nsConn *neffos.NSConn, msg neffos.Message) error {
				log.Printf("disconnected from namespace: %s", msg.Namespace)
				return nil
			},
			"msg": func(nsConn *neffos.NSConn, msg neffos.Message) error {
				return nil
			},
		},
	}

	nativeEvents := neffos.Events{
		neffos.OnNativeMessage: func(c *neffos.NSConn, msg neffos.Message) error {
			log.Printf("Got: %s", string(msg.Body))
			//  ???  to  namespaces   web   msg
                       // Send a message from here to the web namespace msg event
			return nil
		},
	}

	ws := neffos.New(gorilla.Upgrader(gorillaWs.Upgrader{CheckOrigin: func(*http.Request) bool { return true }}), event)

	nativeWs := neffos.New(gorilla.Upgrader(gorillaWs.Upgrader{CheckOrigin: func(*http.Request) bool { return true }}), nativeEvents)

	app.Get("/websocket", websocket.Handler(ws))

	app.Get("/websocket-two", websocket.Handler(nativeWs))

Problem with Broadcasting

@kataras I have Issue with

server.Broadcast(nil,neffos.Message{
To: userID,
Namespace: variable.Agent,
Event:     "notif",
Body:      neffos.Marshal(nf),	
})

i use gobwas.DefaultUpgrader

when i use server.Broadcast in loop it is work but But not very good.
You can test yourself.
i open multiple browser tap with same userId(socketId)

server.brodacast not send all message in loop;

you can see cron example .

c.write(neffos.Message{
To: userID,
Namespace: variable.Agent,
Event:     "notif",
Body:      neffos.Marshal(nf),	
})

write() mthod work very very good
but it send message to one tap (last) with same userId .

I'm going to send it to all the tabs. with same userId

websocket Issue

Great writer kataras :

In the demo given by websocket, the ws open link starts with "http://xxx", and then connect in the way of Upgrade! ! Now the project needs to be compatible with the mobile app and needs the "ws://xxx" style. How can I change it? please help me

[BUG]

Describe the bug
Call GetConnectionsByNamespace appeared abnormal

Note, if that bugs is a browser relative please report it at the neffos.js repository instead.

Screenshots
If applicable, add screenshots to help explain your problem.
image
image
image

Desktop (please complete the following information):

  • OS: UBUNTU
  • Version 18.04.02

[FEATURE REQUEST] Rooms Ask

I've working around with Rooms and has a special case that need to call ask function with message's Room property.

For now, i could use connection's ask function but i think implement ask function for rooms would make a better structure.

At last, thank you for the nice library and framework.

This lib is "production ready" ?

I want to integrate websocket to my project, and I have a plan using nodejs/socket.io. and after looked at the neffos project carefully, I consider using this library.
But does it ready for production now ?
Thank you for contribute many wonderful projects !

[DOCS] Explanation of scaling features

Hello! I'm very interested in using neffos to build a chat application. Scaling has been a concern in the past, and I found neffos after researching scalable websocket implementations in Go.

So I'm trying to wrap my head around how deployment works with neffos if I want a scalable setup. We currently deploy in kubernetes. Is it possible to create a redis cluster, scale up several neffos-based miniservices, and allow any client to connect to any room from any instance of the server? Or do all clients connecting to the same room have to be connected to the same backend instance? That would be fantastic if it's the former.

Thank you

[BUG] Message sent more than once

Describe the bug
Sometimes the message sent arrives more than once on the server side.
I can't reproduce the problem, it is happening with some of my clients, currently between an Android emulator and an Android device, on same network.
Do I need to create some kind of MSG ID, and control messages that arrives more than once? And is that really possible?
Currently, I'm using gobwas Dialers.

To Reproduce

  • Client send messages like this:
	// client sending message - ONCE
	c.Conn.Write(neffos.Message{
		Namespace: "default",
		Room:      "A_ROOM",
		Event:     "AN_EVENT",
		Body:      anyBytesArr,
		SetBinary: true,
	})
  • Server send messages like this:
	// server sending message - TWICE, since it arrives 2 times instead of one
	c.Conn.Server().Broadcast(c, neffos.Message{
		Namespace: "default",
		Room:      "A_ROOM",
		Event:     "AN_EVENT",
		Body:      anyBytesArr,
		SetBinary: true,
	})

Expected behavior
Client send a message once, it arrives just once on server.

Logs
Server logs:
2020/02/07 10:00:34 [e4e6f308-680d-4a36-ae06-e90d5c44c623][Room: A_ROOM][Event: AN_EVENT]
2020/02/07 10:00:34 [e4e6f308-680d-4a36-ae06-e90d5c44c623][Room: A_ROOM][Event: AN_EVENT]

Desktop (please complete the following information):

  • OS: [Linux, Android, GoMobile]
  • Version [Ubuntu 18.04.3 LTS, Android any, Go 1.13.4]

Additional context
I'm using GoMobile to compile the client version and use it on an Android app (yay! works on iOS and Android!)

[BUG][HELP] Will frequent use of Broadcast methods cause block?

Describe the bug
SyncBroadcaster = true // I don’t know why setting it to false will cause some broadcasts to not be sent at the same time, so I set it to true

Hi, @kataras, currently our project has an average of 10 broadcasts per second, and there are 10 users in the room, and there may be more later. When the broadcast method is frequently called, the transmission will be blocked until it returns to normal after a period of time. I don’t know how to debug or optimize the code logic. Can you provide information on how the relevant broadcast methods work, or is there a better way to recommend how to send broadcasts frequently?

[FEATURE REQUEST] export more events and authentication

Is your feature request related to a problem? Please describe.

1, I tried neffos, it works great, but seems it only focused on Message event. But for app layer, maybe want to collect some information about: connection count, each connection startTime and endTime, last activeTime, and so on.

2, send broadcast PING from server to all client. For mobile network, APP may hibernate at background, which can't send PING. so need server to send PING to remain the TCP connection and APP alive.

3, able to do authentication when websockt upgrade, like pass the http.Request and return a simple true or false. so the app layer can read url and param from http.Request. for example: http://xxxx/ws/endpoint?uid=aaa&token=bbb

Describe the solution you'd like
1, export some event for each connection, like: CONNECT, CLOSE, ACTIVE(stand for: message, ping, pong) ......

2, able to broadcast PING

3, export handler for authentication, which default return true

[question] Why brower loop print re-connected and send head endpoint request?

I'm use neffos.js code :

image

But i'm find console loop print re-connected , time is i'm set reconnect , This is keeplive?

image

And find console print send HEAD methods to set endpoint , I'm set iris to Any but is not work

image

The Head request how to success?

I'm client how to keeplive? if use reconnect , I'm iris log all reconnected log , is not good~

thanks for help ~ @kataras

[BUG] the process hang up, CPU become 100%

Describe the bug

I use neffos as WebSocket server to handler all WebSocket connects from browser.
It is running well when first deploy the web Application on production server. However it became weird at some moment. The CPU became 100% usage, and the server could not handle any incoming WebSocket connection. Debugging by dlv tool, I found the neffos conn's goroutine block main process. I don't know how reproduce this bug, but it happened several times on productions environment.

To Reproduce

Expected behavior
I hope how to avoid this situation or this is the bug.

Screenshots
image

image

Desktop (please complete the following information):

  • OS: Centos
  • Version 7

How to monitor client disconnect / close connection?

I want to do something when closing, or interrupting the client connection, such as cleaning up. I call conn.Close() on ws.OnConnect{} without triggering ws.OnDisconnect{}. what should I do?

package main

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

func main() {
    app := iris.Default()
    
    ws := neffos.New(websocket.DefaultGorillaUpgrader, neffos.Namespaces{
    	neffos.OnNativeMessage: neffos.Events{
    	    "login": func(ns *neffos.NSConn, msg neffos.Message) error {
    	        return nil
    	    },
    	},
    })
    
    ws.OnConnect = func(conn *neffos.Conn) error {
        allowConnect = true
        // some operations...
        if !allowConnect {
            conn.Close()
        }
    	return nil
    }
    
    ws.OnDisconnect = func(c *neffos.Conn) {
        // No shutdown signal is received and the method body is not triggered.
    	fmt.Println("OnDisconnect")
    }
    
    app.Get("/login", websocket.Handler(ws))
    app.Logger().Fatal(app.Listen(":8080"))
}

Can you provide more programming language examples

I just use example in https://github.com/kataras/iris/blob/master/_examples/websocket/basic/server.go

After run this server, I cannot access from python script.

The Python Script:

from websocket import create_connection
ws = create_connection("ws://127.0.0.1:8080/echo?X-Websocket-Header-X-Username=ok&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU")
print("Sending 'Hello, World'...")
ws.send("Hello, World")
print("Sent")
print("Receiving...")
result =  ws.recv()
print("Received '%s'" % result)
ws.close()

And the result shows that script can establish a connection, but when the client begins to receive , it shows that "WebSocketConnectionClosedException: Connection is already closed."

This problem also happens when I serve "./broswer/index.html" at another port using another web quick server (the index.html file has been modified so that the websocket port is still the same as server-side, that is, 8080.), it shows that

neffos.min.js:1 WebSocket connection to 'ws://0.0.0.0:8080/echo?X-Websocket-Header-X-Username=jhi&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU' failed: Error during WebSocket handshake: Unexpected response code: 403

So what should I do? And can you help me with a python websocket client script? Because I will use python script to listen and handle the websocket broadcasting message. Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.