GithubHelp home page GithubHelp logo

centrifugal / centrifuge Goto Github PK

View Code? Open in Web Editor NEW
982.0 24.0 88.0 6.02 MB

Real-time messaging library for Go. The simplest way to add feature-rich and scalable WebSocket support to your application. The core of Centrifugo server.

Home Page: https://pkg.go.dev/github.com/centrifugal/centrifuge

License: MIT License

Go 88.03% HTML 9.89% CSS 0.01% JavaScript 0.26% Lua 1.76% Shell 0.04%
scalability websocket pubsub protobuf json messaging real-time redis eventsource http-streaming sse authentication

centrifuge's Introduction

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ Build Status codecov.io GoDoc

This library has no v1 release, API may change. Before v1 release patch version updates only have backwards compatible changes and fixes, minor version updates can have backwards-incompatible API changes. Master branch can have unreleased code. Only two last Go minor versions are officially supported by this library.

Centrifuge library is a real-time core of Centrifugo server. It's a general purpose real-time messaging library for Go programming language. Real-time messaging can help create interactive applications where events are delivered to online users with minimal delay. Chats apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. can all be built on top of Centrifuge library.

The library is built on top of efficient client-server protocol schema and exposes various real-time oriented primitives for a developer. Centrifuge solves problems developers may come across when building complex real-time applications – like scalability to many server nodes, proper persistent connection management and invalidation, fast reconnect with message recovery, WebSocket fallback options (without sticky sessions requirement in distributed scenario).

Library highlights:

  • Fast and optimized for low-latency communication with millions of client connections. See test stand with 1 million connections in Kubernetes
  • WebSocket bidirectional transport using JSON or binary Protobuf formats, both based on a strict Protobuf schema. Code generation is used to push both JSON and Protobuf serialization performance to the limits
  • Our own WebSocket emulation layer over HTTP-streaming (JSON + Protobuf) and Eventsource (JSON) without sticky sessions requirement for distributed setup
  • Possibility to use unidirectional transports without using custom Centrifuge SDK library: see examples for GRPC, EventSource(SSE), HTTP-streaming, Unidirectional WebSocket
  • Built-in horizontal scalability with Redis PUB/SUB, consistent Redis sharding, Redis Sentinel and Redis Cluster support, super-optimized Redis communication layer
  • Effective non-blocking broadcasts towards client connections using individual queues
  • Native authentication over HTTP middleware or custom token-based (ex. JWT)
  • Channel concept to broadcast message to all active subscribers
  • Client-side and server-side channel subscriptions
  • Bidirectional asynchronous message communication, RPC calls, builtin PING/PONG
  • Presence information for channels (show all active clients in a channel)
  • History information for channels (ephemeral streams with size and TTL retention)
  • Join/leave events for channels (aka client goes online/offline)
  • Possibility to register a custom PUB/SUB Broker and PresenceManager implementations
  • Option to register custom Transport, like Centrifugo does with WebTransport
  • Message recovery mechanism for channels to survive PUB/SUB delivery problems, short network disconnects or node restart
  • Out-of-the-box Prometheus instrumentation
  • Client SDKs for main application environments all following single behaviour spec (see list of SDKs below).

Real-time SDK

For bidirectional communication between a client and a Centrifuge-based server we have a set of official client real-time SDKs:

These SDKs abstract asynchronous communication complexity from the developer: handle framing, reconnect with backoff, timeouts, multiplex channel subscriptions over single connection, etc.

If you opt for a unidirectional communication then you may leverage Centrifuge possibilities without any specific SDK on client-side - simply by using native browser API or GRPC-generated code. See examples of unidirectional communication over GRPC, EventSource(SSE), HTTP-streaming, WebSocket.

Explore Centrifuge

Installation

go get github.com/centrifugal/centrifuge

Tutorial

Let's take a look on how to build the simplest real-time chat with Centrifuge library. Clients will be able to connect to a server over Websocket, send a message into a channel and this message will be instantly delivered to all active channel subscribers. On a server side we will accept all connections and will work as a simple PUB/SUB proxy without worrying too much about permissions. In this example we will use Centrifuge Javascript client (centrifuge-js) on a frontend.

Start a new Go project and create main.go:

package main

import (
	"log"
	"net/http"

	// Import this library.
	"github.com/centrifugal/centrifuge"
)

// Authentication middleware example. Centrifuge expects Credentials
// with current user ID set. Without provided Credentials client
// connection won't be accepted. Another way to authenticate connection
// is reacting to node.OnConnecting event where you may authenticate
// connection based on a custom token sent by a client in first protocol
// frame. See _examples folder in repo to find real-life auth samples
// (OAuth2, Gin sessions, JWT etc).
func auth(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		// Put authentication Credentials into request Context.
		// Since we don't have any session backend here we simply
		// set user ID as empty string. Users with empty ID called
		// anonymous users, in real app you should decide whether
		// anonymous users allowed to connect to your server or not.
		cred := &centrifuge.Credentials{
			UserID: "",
		}
		newCtx := centrifuge.SetCredentials(ctx, cred)
		r = r.WithContext(newCtx)
		h.ServeHTTP(w, r)
	})
}

func main() {
	// Node is the core object in Centrifuge library responsible for
	// many useful things. For example Node allows publishing messages
	// into channels with its Publish method. Here we initialize Node
	// with Config which has reasonable defaults for zero values.
	node, err := centrifuge.New(centrifuge.Config{})
	if err != nil {
		log.Fatal(err)
	}

	// Set ConnectHandler called when client successfully connected to Node.
	// Your code inside a handler must be synchronized since it will be called
	// concurrently from different goroutines (belonging to different client
	// connections). See information about connection life cycle in library readme.
	// This handler should not block – so do minimal work here, set required
	// connection event handlers and return.
	node.OnConnect(func(client *centrifuge.Client) {
		// In our example transport will always be Websocket but it can be different.
		transportName := client.Transport().Name()
		// In our example clients connect with JSON protocol but it can also be Protobuf.
		transportProto := client.Transport().Protocol()
		log.Printf("client connected via %s (%s)", transportName, transportProto)

		// Set SubscribeHandler to react on every channel subscription attempt
		// initiated by a client. Here you can theoretically return an error or
		// disconnect a client from a server if needed. But here we just accept
		// all subscriptions to all channels. In real life you may use a more
		// complex permission check here. The reason why we use callback style
		// inside client event handlers is that it gives a possibility to control
		// operation concurrency to developer and still control order of events.
		client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) {
			log.Printf("client subscribes on channel %s", e.Channel)
			cb(centrifuge.SubscribeReply{}, nil)
		})

		// By default, clients can not publish messages into channels. By setting
		// PublishHandler we tell Centrifuge that publish from a client-side is
		// possible. Now each time client calls publish method this handler will be
		// called and you have a possibility to validate publication request. After
		// returning from this handler Publication will be published to a channel and
		// reach active subscribers with at most once delivery guarantee. In our simple
		// chat app we allow everyone to publish into any channel but in real case
		// you may have more validation.
		client.OnPublish(func(e centrifuge.PublishEvent, cb centrifuge.PublishCallback) {
			log.Printf("client publishes into channel %s: %s", e.Channel, string(e.Data))
			cb(centrifuge.PublishReply{}, nil)
		})

		// Set Disconnect handler to react on client disconnect events.
		client.OnDisconnect(func(e centrifuge.DisconnectEvent) {
			log.Printf("client disconnected")
		})
	})

	// Run node. This method does not block. See also node.Shutdown method
	// to finish application gracefully.
	if err := node.Run(); err != nil {
		log.Fatal(err)
	}

	// Now configure HTTP routes.

	// Serve Websocket connections using WebsocketHandler.
	wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{})
	http.Handle("/connection/websocket", auth(wsHandler))

	// The second route is for serving index.html file.
	http.Handle("/", http.FileServer(http.Dir("./")))

	log.Printf("Starting server, visit http://localhost:8000")
	if err := http.ListenAndServe(":8000", nil); err != nil {
		log.Fatal(err)
	}
}

Also create file index.html near main.go with content:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <script type="text/javascript" src="https://unpkg.com/centrifuge@^5/dist/centrifuge.js"></script>
        <title>Centrifuge chat example</title>
    </head>
    <body>
        <input type="text" id="input" />
        <script type="text/javascript">
            function drawText(text) {
                const div = document.createElement('div');
                div.innerHTML = text + '<br>';
                document.body.appendChild(div);
            }
            
            // Create Centrifuge object with Websocket endpoint address set in main.go
            const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');

            centrifuge.on('connected', function(ctx){
                drawText('Connected over ' + ctx.transport);
            });
            
            const sub = centrifuge.newSubscription("chat");
            sub.on('publication', function(ctx) {
                drawText(JSON.stringify(ctx.data));
            });
            // Move subscription to subscribing state.
            sub.subscribe();
            
            const input = document.getElementById("input");
            input.addEventListener('keyup', function(e) {
                if (e.key === "ENTER") {
                    e.preventDefault();
                    sub.publish(this.value);
                    input.value = '';
                }
            });
            // After setting event handlers – initiate actual connection with server.
            centrifuge.connect();
        </script>
    </body>
</html>

Then run Go app as usual:

go run main.go

Open several browser tabs with http://localhost:8000 and see chat in action.

While this example is only the top of an iceberg, it should give you a good insight on library API. Check out examples folder for more. We recommend to start looking from chat_json example, which extends the basics shown here and demonstrates more possibilities of Centrifuge protocol:

Chat example

Important

Keep in mind that Centrifuge library is not a framework to build chat applications. It's a general purpose real-time transport for your messages with some helpful primitives. You can build many kinds of real-time apps on top of this library including chats but depending on application you may need to write business logic yourself.

Tips and tricks

Some useful advices about library here.

Connection life cycle

Let's describe some aspects related to connection life cycle and event handling in Centrifuge:

  • If you set middleware for transport handlers (like WebsocketHandler) – then it will be called first before a client sent any command to a server and handler had a chance to start working. Just like a regular HTTP middleware. You can put Credentials to Context to authenticate connection.
  • node.OnConnecting called as soon as client sent Connect command to server. At this point no Client instance exists. You have incoming Context and Transport information. You still can authenticate Client at this point (based on string token sent from client side or any other way). Also, you can add extra data to context and return modified context to Centrifuge. Context cancelled as soon as client connection closes. This handler is synchronous and connection read loop can't proceed until you return ConnectReply.
  • node.OnConnect then called (after a reply to Connect command already written to connection). Inside OnConnect closure you have a possibility to define per-connection event handlers. If particular handler not set then client will get ErrorNotAvailable errors requesting it. Remember that none of event handlers available in Centrifuge should block forever – do minimal work, start separate goroutines if you need blocking code.
  • Client initiated request handlers called one by one from connection reading goroutine. This includes OnSubscribe, OnPublish, OnPresence, OnPresenceStats, OnHistory, client-side OnRefresh, client-side OnSubRefresh.
  • Other handlers like OnAlive, OnDisconnect, server-side OnSubRefresh, server-side OnRefresh called from separate internal goroutines.
  • OnAlive handler must not be called after OnDisconnect.
  • Client initiated request handlers can be processed asynchronously in goroutines to manage operation concurrency. This is achieved using callback functions. See concurrency example for more details.

Channel history stream

Centrifuge Broker interface supports saving Publication to history stream on publish. Depending on Broker implementation this feature can be missing though. Builtin Memory and Redis brokers support keeping Publication stream.

When using default MemoryBroker Publication stream kept in process memory and lost as soon as process restarts. RedisBroker keeps Publication stream in Redis LIST or STREAM data structures – reliability inherited from Redis configuration in this case.

Centrifuge library publication stream not meant to be used as the only source of missed Publications for a client. It mostly exists to help many clients reconnect at once (load balancer reload, application deploy) without creating a massive spike in load on your main application database. So application database still required in idiomatic use case.

Centrifuge message recovery protocol feature designed to be used together with reasonably small Publication stream size as all missed publications sent towards client in one protocol frame on resubscribe to channel.

Logging

Centrifuge library exposes logs with different log level. In your app you can set special function to handle these log entries in a way you want.

// Function to handle Centrifuge internal logs.
func handleLog(e centrifuge.LogEntry) {
	log.Printf("%s: %v", e.Message, e.Fields)
}

cfg := centrifuge.DefaultConfig
cfg.LogLevel = centrifuge.LogLevelDebug
cfg.LogHandler = handleLog

Allowed origin for WebSocket

When connecting to Centrifuge WebSocket endpoint from web browsers you need to configure allowed Origin. This is important to prevent CSRF-like/WebSocket hijacking attacks. See this post for example.

By default, CheckOrigin function of WebSocket handler will ensure that connection request originates from same host as your service. To override this behaviour you can provide your own implementation of CheckOrigin function to allow origins you trust. For example, your Centrifuge runs on http://localhost:8000 but you want it to allow WebSocket connections from http://localhost:3000:

centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{
	CheckOrigin: func(r *http.Request) bool {
		originHeader := r.Header.Get("Origin")
		if originHeader == "" {
			return true
		}
		return originHeader == "http://localhost:3000"
	},
})

Note, that if WebSocket Upgrade does not contain Origin header – it means it does not come from web browser and security concerns outlined above are not applied in that case. So we can safely return true in this case in the example above.

CORS for HTTP-based transports

Centrifuge has two HTTP-based fallback transports for WebSocket – see HTTPStreamHandler and SSEHandler. To connect to those from web browser from the domain which is different from your transport endpoint domain you may need to wrap handlers with CORS middleware:

func CORS(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		header := w.Header()
		if originAllowed(r) {
			header.Set("Access-Control-Allow-Origin", r.Header.Get("origin"))
			if allowHeaders := r.Header.Get("Access-Control-Request-Headers"); allowHeaders != "" && allowHeaders != "null" {
				header.Add("Access-Control-Allow-Headers", allowHeaders)
			}
			header.Set("Access-Control-Allow-Credentials", "true")
		}
		h.ServeHTTP(w, r)
	})
}

http.Handle("/connection/http_stream", CORS(centrifuge.NewHTTPStreamHandler(node, centrifuge.HTTPStreamHandlerConfig{})))

You can also configure CORS on load-balancer/reverse-proxy level.

For contributors

Running integration tests locally

To run integration tests over Redis, Redis + Sentinel, Redis Cluster:

docker compose up
go test -tags integration ./...

To clean up container state:

docker compose down -v

centrifuge's People

Contributors

aldan95 avatar alkanna avatar amir-mamedov avatar datalinke avatar dependabot[bot] avatar drabuna avatar filonenko-mikhail avatar flymedllva avatar fzambia avatar j178 avatar kspacer201 avatar masterada avatar shyce avatar skarm avatar tie avatar tufanbarisyildirim avatar vitoordaz avatar whiteforestzx avatar wlredeye 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

centrifuge's Issues

Custom disconnect code invalid

Code int `json:"-"`

type Disconnect struct {
	// Code is disconnect code.
	Code int `json:"-"`
	// Reason is a short description of disconnect.
	Reason string `json:"reason"`
	// Reconnect gives client an advice to reconnect after disconnect or not.
	Reconnect bool `json:"reconnect"`
}

document, we can return a custom broken link error
https://centrifugal.github.io/centrifugo/server/proxy/#return-custom-disconnect

But the code that actually returns forever is 0 because JSON data is ignored here.
Why should our Code field ignored from json ?

Publish not working to channels with ":" in name.

I have a few channels that my users join on login:

  • lobby
  • location:ABCD
  • location:EFGH
  • session:123

The only one that I can node.Publish() per the Redis example is "lobby" -> I believe because of the ":" in the name to namespace my channels.

Any chance I'm just doing this wrong?

Support go mod

Now when go 1.12 is out, I suppose we can experiment with go mod support for centrifuge package.

Chat_json example doesn’t work

Chat_json example doesn’t work, a0f7b99 commit.

# command-line-arguments cmd/main.go:94:3: unknown field 'HistoryMetaTTL' in struct literal of type centrifuge.MemoryEngineConfig cmd/main.go:151:92: e.Method undefined (type centrifuge.RPCEvent has no field or method Method) cmd/main.go:198:11: assignment mismatch: 2 variables but node.Publish returns 1 values

go.mod

`
go 1.13

require (
github.com/centrifugal/centrifuge v0.7.0
github.com/prometheus/client_golang v1.6.0
)
`

Multi tenant support

Hi!

We have been using centrifuge in production for more than a year now, without any issues, so thanks for the awesome project. A couple weeks ago we started launching multiple websites using our api, and we need to separate them completely. Our api was desigend for this, so it was no issue, but we need to separate centrifuge channels as well.

My first idea was:

  • user subscribes to some:channel
  • in centrifuge, I add support for multiple site, and modify the channel name to some:<somesite>.channel (eg: based on a header, or some values encoded in the jwt token)
  • when messages arrive on the some:<somesite>.channel, i send them out as some:channel

and although it would be best for us, I found no mechanism in centrifuge where a channel name is rewritten, so this approach would be very difficult for me to implement. If you think this is a good idea I would still love for it to happen, but until then I found a different approach.

My second idea:

  • user subscibes to some:<somesite>.channel
  • I validate that the user belongs to the site (eg: based on some values encoded in the jwt token)

I could almost implement this using centrifuge as a library (instead of using centrifugo server). I made a handler for the client.Subscribed event. The only thing I miss is to be able to access the JWT token info in the client.On().Subscribe handler. I made a fork exposing the client.info, with that it works perfectly.

Introduce subscription ID

At moment Centrifuge does not allow to subscribe on the same channel twice inside single connection. This is ok for most use cases but leads to difficulties and non very clear API restrictions on client side (users can only create one subscription to the same channel).

I believe introducing incremental subscription ID should solve this.

broker example not displaying web gui

see:
https://github.com/centrifugal/centrifuge/blob/master/_examples/custom_broker/main.go#L137

I just get a browser list of files at localhost:8000

  • sure nothing else is running :)

Reprod:

# nats
gnatsd

---

## redis
ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents  # Enable Redis autostart

launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist  # Start Redis server via launchctl
# homebrew.mxcl.redis.plist contains reference to redis.conf file location: /usr/local/etc/redis.conf 

redis-server /usr/local/etc/redis.conf # Start Redis server using configuration file, Ctrl+C to stop
redis-cli ping # Check if the Redis server is running

launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.redis.plist # Disable Redis autostart


---


# broker example
GO111MODULE=on
cd ./_examples/custom_broker && go run main.go

command ID required for commands with reply expected

I want to mock centrifugo for unit tests and i stucked on this error:
command ID required for commands with reply expected: map[client:7e9210d6-24ca-4508-b977-9c0a4fe0f61d user:]
Error can be reproduced with this server https://github.com/centrifugal/centrifuge/tree/master/examples/chat_json and this client https://github.com/centrifugal/centrifuge-go/blob/master/examples/private.go, with some correction of course. Dockerized centrifugo working perfectly. How to solve this error? Thanks.

How to publish and subscribe to multiple channels?

Hello, I'm currently working on a use-case wherein:

  1. a client should be able to publish to one or more channels (at the same time) and a subscriber should be able to subscribe to one or many channels (at the same time).
  2. publish to all subscribers without a client publish. Something like a PUT call with the channel name and the message to publish.

Is this possible? Any directions for this use-case?

Also, I see two handers NewWebsocketHandler and NewSockjsHandler. is WebsocketHandler a pure websocket handler with no means to downgrade to other methods where WS is not supported? So, if thats the case do you suggest using SockjsHandler in these scenarios?

Swappable Persistence Backends?

I'm curious if making the persistence backends swappable is on the roadmap for this library.

In my specific use case, it would be nice to use the lightweight Bolt for persistence while still utilizing centrifuge's pub sub system, unlike the way you have it set up with Redis now which seems to require using Redis's pub/sub system if you want persistence.

If there was an API for writing pluggable backends, I'd happily get started on one for bolt.

Set Client.user w/o Disconnect/Connect events

Hello!
I implement the user authentication process completely with WebSockets.
As I understand it, changing Client.user now is possible only at node.On().ClientConnecting method (by return Credentials at ConnectReply struct).
But Singin reguest is possible only after the Connecting event. And for add UserID to Client.user i have emit Disconnect/Connect events. I would like to avoid this.
Is it possible to add Client.SetUserID method like this:

// SetUserID set user ID.
func (c *Client) SetUserID(userID string) {
	c.mu.Lock()
	c.user = userID
	c.mu.Unlock()
}

P.S.: I have studied the source code and did not see difficulty when applying sharding with Client.SetUserID method.

Centrifuge with 16S database

Hi,

I was wondering if you could help us with the following problem that we encounter when using Centrifuge classification: We wanted to classify our 16S rRNA reads sequence by Oxford Nanopore, and used Centrifuge and Kraken2 to do so while building a NCBI database based on all bacterial data and a 16S database based on the Silva database for both classification tools.
Whereas Kraken2 is able to classify most reads (>90%) with both bacterial NCBI and 16S database, Centrifuge only classifies comparably many reads if we take the bacterial NCBI database. In the case of the 16S database, very few reads (~10%) can be classified. I was wondering if you have any explanation for this effect?

Many thanks for your help!

Best regards,
Lara

Sec-Websocket-Protocol response, perhaps with custom upgrader

I'm working on an application that provides updates to logged in users via web-socket. I don't even want the connection to be allowed for non-logged-in users and the way this was solved previously was to use the Sec-Websocket-Protocol header in the original request (setting that to a JWT).

I've added the same thing with centrifuge and added the UserID to centrifuge just fine, but the issue is that I can't make centrifuge reply with the protocol header set, which results in errors like the following:

WebSocket connection to 'ws://localhost:5000/v1/web-sockets-pubsub' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received

Firefox seems fine with it and everything works well there.

As far as I know (from here) there's no other way to add auth, apart from adding the JWT to the URL but for some reason I'm trying to avoid it. I don't have a clear argument against it but something doesn't seem right to put tokens in the URL. Server and front-end are on different subdomains.

It would be great to have the option to add a customer gorilla *websocket.Upgrader to NewWebsocketHandler(), maybe by passing it as an option to WebsocketConfig. If it's null, the default is used, otherwise the given one.

What do you think?

Or am I thinking about this wrong, is there another way to handle authentication that I'm missing?

Disconnect if PublishOnHistoryAdd is set to false

With v0.8.0, if PublishOnHistoryAdd is set to false (it was set to false by mistake), i keep getting disconnected with reason insufficient state. I think this is because the offset magic:

if ARGV[4] ~= '' then
	local payload = "__" .. offset .. "__" .. ARGV[1]
	redis.call("publish", ARGV[4], payload)

is missing from engine_redis.Publish

Passing headers

Hello ,How to pass authorisation headers from the client side when subscribing or publishing message?

Redis engine TLS support

Hello,

I'm wondering if you are planning (or maybe it's already implemented) to add a support for redis TLS. Looks like it's supported by redigo.

I think this would be really valuable feature.

I'm willing to make a PR for this feature.

Extend history streaming API

At moment Centrifuge library can recover missed messages automatically. Also it has API to get all publications in history limited by size and TTL.

I want to try extending history API to give a possibility to paginate over stream from certain position with limit.

Steps to make this possible:

  1. Integrate with Redis Streams
  2. Get rid of Seq and Gen uint32 fields and replace it with single uint64 Offset field. This should simlify working with API from outside. Javascript can't work with numbers greater than 2**53-1 but my calculations showed that if we publish 1000 messages per second to channel than we have about 285616 years until we hit that limit
  3. I also want to fully get rid of Protobuf protocol aliases available as part of public API at moment in protocol.go

sockJSHandler NotifyShutdown called immediately when new connection attempted

Hi there,

a recent upgrade has broken my sockjs handler....

--

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x190 pc=0x97f716]

goroutine 84 [running]:
github.com/centrifugal/centrifuge.(*Node).NotifyShutdown(...)
	[REMOVED]go/src/github.com/centrifugal/centrifuge/node.go:222
github.com/centrifugal/centrifuge.(*SockjsHandler).sockJSHandler.func1(0xc73d00, 0xc0005b66e0, 0xc0001fec40)
	[REMOVED]go/src/github.com/centrifugal/centrifuge/handler_sockjs.go:170 +0x186
created by github.com/centrifugal/centrifuge.(*SockjsHandler).sockJSHandler
	[REMOVED]go/src/github.com/centrifugal/centrifuge/handler_sockjs.go:163 +0xcc

How do i add jwt tokens on channel or rooms in javascript client and server

Sorry my bad english.
I am confused because poor of tutorial example in youtube.
I am confused between token for client and token for server centrifuge like this:

var centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket');

centrifuge.setToken(YOUR_TOKEN);

centrifuge.subscribe("news", function(message) {
    console.log(message);
});

centrifuge.connect();

How do i add token jwt in javascript client

var centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket');

centrifuge.subscribe("news", function(message) {
    console.log(message);
});

centrifuge.connect();

History

Hi,A client when subscribing to a channel needs to get last n messages published in that channel.
How can I handle this use case?Any reference for that?
centrifuge-js is the client library I am using.

Duplicate messages in history

I have set history time limit to 1 hour in server.
When a client subscribes to channel, he gets the messages.
When he unsubscribes and again subscribes within 1 hour ,he will get some messages twice right.How to avoid reception of duplicate messages?

[feature request] auth interface

Can u abstract the method in /internal/auth/auth.go as an interface, so is it convenient for users to use this library to verify logic with their own auth sys? For example, access to the existing user system or redis's session.

Option to expire sequence number for channel in Redis

At moment once set the sequence number will live forever in Redis. Looks like we can set a reasonable time when this key can safely expire if the channel have not been active for a long time - for example in a time that is much bigger than channel history retention period.

How to build?

try with

git clone https://github.com/centrifugal/centrifuge.git
dep ensure 

with no luck(
it clones to centrifuge but depend from centrifugal/centrifuge
Must i clone all projects? Witch branches?

Move JWT parsing logic out of the library

This will let users to provide own token authentication and allow to remove hard dependency to github.com/dgrijalva/jwt-go. Also need to update current examples and include function that mimics current behaviour for simple migration.

And seems like it's time to add changelog file.

Support user online information that is not related to certain channel

Currently Centrifuge/Centrifugo can show instant client presence information for channels. Though it's not effective for cases when client only subscribed on personal channel but still needs info about other users online status. Maybe it's reasonable to add new engine method:

UsersOnline(userIDs []string) (map[string]*UserOnlineInfo, error)

Where UserOnlineInfo is sth like this:

type UserOnlineInfo struct {
    Updated uint32
}

It will return approximation (in a configured window) of user state based on their presence in channels where new user_online option set (or maybe it should just be top-level option without any relation to channels but this will also require adding new method AddUserOnline to handle cases when client not subscribed to any channel which is pretty common situation in Centrifuge library case). Maybe this should be Credentials flag (or connection token claim in Centrifugo case).

Information should be sharded by userID, this is different from how other things sharded at moment (by channel).

This will allow apps to ask state of certain users. I think this can only be available as part of server API and not exposed to client protocol. Apps that use Centrifugo will be able to call it from backend, apps that use Centrifuge lib will be able to do whatever they want - for example make it one of RPC calls.

This is just an idea at moment, maybe it's not worth adding - but let's think about it.

Feature request: connect without token

In insecure mode it's possible to connect to centrifuge without specifying a token. But the same insecure option disables a lot of other security checks as well.

I suggest adding a separate flag for allowing connect without token (but still only allowing the connected user to log into anonym channels).

Deprecate and then remove Engine Channels method

Channels method of Engine (Broker part) returns a slice of all channels active throughout all Centrifugo nodes. This data is eventually consistent in case of Redis, not supported in Redis cluster case, not supported by most other PUB/SUB brokers, does not scale well for millions of channels thus can lead to error prone architectural decisions in applications that use Centrifuge or Centrifugo. I am considering to remove it in Centrifuge library Engine interface and in Centrifugo v3.

Maybe this sort of data should be extracted not via broker but over Centrifuge node cluster survey. But even in this case it won't scale well for business scenarios (should only be used as instrument of observability).

reporting node health

How is node health reported? Looked around in the code and couldn't see anything obvious.

centrifugo's api has no healthcheck endpoint, while investigating how to create one I noticed that health would need to be reported by each node. Is that correct?

To start simple we could just ping the configured engine, then report "ok" or "not ok".

I'm willing to send a PR, but would probably need a little guidance to get me started.

Great project btw.
Thanks!

Increase test coverage

Let's close this when we reach at least 83%. Really appreciate any help with test coverage.

Reliable node unsubscribe from PUB/SUB channel

At moment if node unsubscribes from Redis channel (last client leaves channel) and an error happens during unsubscribe request to Redis then subscription to channel can stay alive. This does not affect general behaviour but can lead to stale subscriptions and receiving non-actual messages over PUB/SUB which are then dropped on node level. It would be nice to clean up such subscriptions. One idea is to have a sort of unsubscribe unbounded queue which is consumed by workers to reliably process unsubscriptions.

Get the Admin GUI and Example(s) running at the same time

At the moment it is hard to run an example and then see whats going on.
So having the Admin GUI run at the same time is a good start.

Here is my suggestion for what we need to do:

  1. Get Admin GUI running with an example.
  2. Use chat-Protobuf as the example.
  • BTW the Web gui does not work for the Chat_protobuf example.
  • I tracked the issue down to caching. Had to turn off caching when dev tools is open just to keep working.

Optional but just putting it here for now :)
How to have all examples running at the same time:
https://github.com/pion/webrtc/tree/master/examples
Its just an idea. Maybe for the next iteration of this issue if we feel its helpful. Might not too.

Decouple Centrifugo specific channel namespace configuration from library core

At moment Centrifuge library follows Centrifugo in terms of subscribe and publish permissions. While this is fine for Centrifugo - having namespaces and predefined channel rules inside a library can be a bit confusing for users.

What I want to achieve with this library is make it only a reasonable wrapper over client-server protocol without many opinionated logic on top of it. I.e. give developers a possibility to define their own channel rules and permission checks.

Another confusing thing in my opinion is that if we don't define On().Subscribe() handler then client will be able to subscribe on all channels. I believe the default behaviour should be: not allow subscription at all if subscribe handler not set for connection.

That said I'd want to decouple namespace and channel permission logic from Centrifuge library core since it adds a very opinionated layer to library. Maybe this process will also require decoupling JWT parsing logic too.

Extend PublishEvent

Good time of day.
Is it possible to add StreamPosition to a PublishEvent?

client.On().Publish(func(e centrifuge.PublishEvent) centrifuge.PublishReply {
...
offset := e.StreamPosition.Offset
...
}

Message delivery ACK

Does this support delivery ACK ?

My use case is that a user has many devices and I want to know that it was delivered to one of those devices AND cancel the delivery to their other devices.
So this use case is really only for notifications.

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.