├── FAQ.md ├── LICENSE ├── README.md ├── SUMMARY.md ├── assets ├── images │ ├── intro.png │ ├── scheme.png │ ├── scheme_redis.png │ └── web.gif └── styles │ └── website.css ├── book.json ├── clients ├── README.md ├── javascript.md └── mobile_and_other.md ├── deploy ├── README.md ├── certificates.md ├── docker.md ├── nginx.md ├── packages.md ├── sentinel.md └── tuning.md ├── libraries └── README.md ├── mixed ├── README.md ├── delivery_model.md ├── exclude_sender.md ├── insecure_modes.md ├── ping.md ├── private_channels.md └── websocket_compression.md ├── server ├── README.md ├── advanced_configuration.md ├── api.md ├── channels.md ├── client_protocol.md ├── configuration.md ├── connection_check.md ├── engines.md ├── migrate.md ├── recover.md ├── scaling.md ├── settings.md ├── signals.md ├── start.md ├── stats.md └── tokens_and_signatures.md └── web └── README.md /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | Answers on various questions here. 4 | 5 | ### How many connections can one Centrifugo instance handle? 6 | 7 | This depends on many factors. Hardware, message rate, size of messages, channel options enabled, client distribution over channels, is websocket compression enabled or not etc. So no certain answer on this question exists. Common sense, tests and monitoring can help here. Generally we suggest to not put more than 50-100k clients on one node - but you should measure. 8 | 9 | ### Can Centrifugo scale horizontally? 10 | 11 | Yes, it can. It can do this using builtin Redis Engine. Redis is very fast – for example 12 | it can handle hundreds of thousands requests per second. This should be OK for most 13 | applications in internet. But if you are using Centrifugo and approaching this limit 14 | then it's possible to add sharding support to balance queries between different Redis 15 | instances. 16 | 17 | ### Centrifugo stops accepting new connections, why? 18 | 19 | The most popular reason behind this is reaching open file limit. Just make it higher. 20 | 21 | ### Can I use Centrifugo without reverse-proxy like Nginx before it? 22 | 23 | Yes, you can - Go standard library designed to allow this. But proxy before Centrifugo can 24 | be very useful for load balancing clients for example. 25 | 26 | ### Does Centrifugo work with HTTP/2? 27 | 28 | Yes, Centrifugo works with HTTP/2. 29 | 30 | You can disable HTTP/2 running Centrifugo server with `GODEBUG` environment variable: 31 | 32 | ``` 33 | GODEBUG="http2server=0" centrifugo -c config.json 34 | ``` 35 | 36 | ### Is there a way to use single connection to Centrifugo from different browser tabs? 37 | 38 | If underlying transport is HTTP-based and you use HTTP/2 then this will work automatically. 39 | In case of websocket connection there is a way to do this using `SharedWorker` object. 40 | 41 | ### What if I need to send push notifications to mobile or web applications? 42 | 43 | Sometimes it's confusing to see a difference between real-time messages and push notifications. 44 | Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple 45 | iOS devices via APNS, Android devices via GCM or browsers over Web Push API. This is a goal for 46 | another software. But the reasonable question here is how can I know when I need to send real-time 47 | message to client online or push notification to its device because application closed at client's 48 | device at moment. The solution is pretty simple. You can keep critical notifications for client in 49 | database. And when client read message send ack to your backend marking that notification as read 50 | by client, you save this ack too. Periodically you can check which notifications were sent to clients 51 | but they have not read it (no ack received). For such notification you can send push notification 52 | to its device using your own or another open-source solution. 53 | 54 | ### Can I know message was really delivered to client? 55 | 56 | You can but Centrifugo does not have such API. What you have to do to ensure your client received 57 | message is sending confirmation ack from your client to your application backend as soon as client 58 | processed message coming from Centrifugo channel. 59 | 60 | ### Can I publish new messages over websocket connection from client? 61 | 62 | Centrifugo designed to stream messages from server to client. Even though it's possible to 63 | publish messages into channels directly from client (when `publish` channel option enabled) - 64 | we strongly discourage this in production usage as those messages will go through Centrifugo 65 | without any control. Of course Centrifugo could resend those message to your application 66 | endpoint but it would be very inefficient and much worse than just sending new events from 67 | client to your backend. 68 | 69 | So in general when user generates an event it must be first delivered to your app backend 70 | using a convenient way (for example AJAX POST request for web application), processed on 71 | backend (validated, saved into main database) and then published to Centrifugo using 72 | Centrifugo HTTP API or Redis queue. 73 | 74 | Sometimes publishing from client directly into channel can be useful though - for personal 75 | projects, for demonstrations (like we do in our [examples](https://github.com/centrifugal/examples)) or if you trust your users and want 76 | to build application without backend. In all cases when you don't need any message control 77 | on your backend. 78 | 79 | ### How to create secure channel for two users only (private chat case)? 80 | 81 | There are several ways to achieve it: 82 | 83 | * use private channel (starting with `$`) - every time user will try to subscribe on it your backend should provide sign to confirm that subscription request. Read more in [special chapter about private channels](https://fzambia.gitbooks.io/centrifugal/content/mixed/private_channels.html) 84 | * next is [user limited channels](https://fzambia.gitbooks.io/centrifugal/content/server/channels.html#user-channel-boundary) (with `#`) - you can create channel with name like `dialog#42,567` to limit subscribers only to user with id `42` and user with ID `567` 85 | * finally you can create hard to guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations. 86 | 87 | ### What's a best way to organize channel configuration? 88 | 89 | In most situations your application need several real-time features. We suggest to use 90 | namespaces for every real-time feature if it requires some option enabled. 91 | 92 | For example if you need join/leave messages for chat app - create special channel namespace 93 | with this `join_leave` option enabled. Otherwise your other channels will receive join/leave 94 | messages too - increasing load and traffic in system but not actually used by clients. 95 | 96 | The same relates to other channel options. 97 | 98 | ### Can I rely on Centrifugo and its message history for guaranteed message delivery? 99 | 100 | No - Centrifugo is best-effort transport. This means that if you want strongly guaranteed 101 | message delivery to your clients then you can't just rely on Centrifugo and its message 102 | history cache. In this case you still can use Centrifugo for real-time but you should 103 | build some additional logic on top your application backend and main data storage to 104 | satisfy your guarantees. 105 | 106 | Centrifugo can keep message history for a while and you can want to rely on it for 107 | your needs. Centrifugo is not designed as data storage - it uses message history mostly 108 | for recovering missed messages after short client internet connection disconnects. It's 109 | not designed to be used to sync client state after being offline for a long time - this 110 | logic should be on your app backend. 111 | 112 | ### I have not found an answer on my question here: 113 | 114 | We have [gitter chat room](https://gitter.im/centrifugal/centrifugo) - welcome! 115 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Centrifugal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Centrifugo real-time messaging server and its friends 2 | 3 | This is a documentation for Centrifugo v1. **Current version of Centrifugo is v2** - see its [documentation](https://centrifugal.github.io/centrifugo/). 4 | 5 | Centrifugal [organization](https://github.com/centrifugal) provides a set of tools to add real-time features to your web/mobile/desktop application. It brings together several repositories linked by a common purpose – give you a complete and ready to use solution when you want to add real-time events into your application. 6 | 7 | Chats, real-time charts, notifications, various counters and even games can be built using our instruments – [real-time messaging server](server/README.md), [javascript browser client](client/README.md) and [client API libraries](libraries/README.md) for your favorite language. 8 | 9 | Centrifugo server easily integrates with your existing application – no need to change your project architecture and philosophy to get real-time events. Centrifugo server is language agnostic – its API can be used from any programming language. For some popular programming languages (see below) we provide helper libraries, but protocol is open and simple - so it's possible to create your own wrapper. 10 | 11 | On client side users of your application communicate with real-time Centrifugo over Websocket or [SockJS](https://github.com/sockjs/sockjs-client) library protocol. SockJS fallback transports provide real-time messaging support even in old (like IE 8) or mobile browsers. 12 | 13 | Documentation translations: 14 | 15 | * [Chinese language](https://github.com/xurwxj/centrifugal_cn_doc) by [@xurwxj](https://github.com/xurwxj) 16 | 17 | ### Simplified scheme 18 | 19 | ![scheme](https://raw.githubusercontent.com/centrifugal/documentation/master/assets/images/scheme.png) 20 | 21 | ### Our projects 22 | 23 | Let's see which projects Centrifugal organization has: 24 | 25 | * [centrifugo](https://github.com/centrifugal/centrifugo) - real-time messaging server 26 | written in Go language 27 | * [centrifuge-js](https://github.com/centrifugal/centrifuge-js) - Javascript client to 28 | connect to messaging server from web browser. 29 | * [centrifuge-android](https://github.com/centrifugal/centrifuge-android) - Java library to communicate 30 | with Centrifugo client API over Websockets from Android devices. 31 | * [centrifuge-ios](https://github.com/centrifugal/centrifuge-ios) - Swift library to communicate 32 | with Centrifugo client API over Websockets from iOS devices. 33 | * [centrifuge-go](https://github.com/centrifugal/centrifuge-go) - Go library to communicate 34 | with Centrifugo client API over Websockets from Go apps. 35 | * [centrifuge-java](https://github.com/donald-jackson/centrifuge-java) - Java library to communicate 36 | with Centrifugo client API over Websockets from Java applications. 37 | * [centrifuge-mobile](https://github.com/centrifugal/centrifuge-go) - Go client and experimental 38 | bindings generated for iOS and Android using gomobile project. 39 | * [centrifuge-python](https://github.com/centrifugal/centrifuge-go) - Python client for Centrifugo on 40 | top of asyncio library (work in progress). 41 | * [cent](https://github.com/centrifugal/cent) - Python tools to communicate with Centrifugo API. 42 | * [adjacent](https://github.com/centrifugal/adjacent) - a small wrapper over Cent to 43 | simplify real-time server integration with Django framework. 44 | * [rubycent](https://github.com/centrifugal/rubycent) - Ruby gem to communicate 45 | with Centrifugo server API. 46 | * [phpcent](https://github.com/centrifugal/phpcent) - PHP client to communicate 47 | with Centrifugo server API. 48 | * [gocent](https://github.com/centrifugal/gocent) - Go client to communicate 49 | with Centrifugo server API. 50 | * [jscent](https://github.com/centrifugal/jscent) - NodeJS client to communicate 51 | with Centrifugo server API. 52 | * [web](https://github.com/centrifugal/web) - admin web interface for Centrifugo. 53 | Built on ReactJS. 54 | 55 | ### Demo 56 | 57 | We maintain actual [demo of Centrifugo server instance](https://centrifugo.herokuapp.com) on Heroku (password `demo`). 58 | 59 | You can use it to discover Centrifugo without installing it on your computer. 60 | 61 | There are 3 endpoints of demo available: 62 | 63 | * wss://centrifugo.herokuapp.com/connection/websocket - raw Websocket endpoint 64 | * https://centrifugo.herokuapp.com/connection - SockJS endpoint 65 | * https://centrifugo.herokuapp.com/api/ - HTTP API endpoint 66 | 67 | So you can use our client and API libraries to communicate with this demo. 68 | 69 | ### Examples 70 | 71 | Examples can be found [in repo on Github](https://github.com/centrifugal/examples). 72 | 73 | At moment we have the following examples: 74 | 75 | * django – example shows how to integrate Django application with Centrifugal stack 76 | * Tornado application – shows some general aspects of Centrifugal stack using Tornado server - token generation, private channel signing. 77 | * NodeJS example - shows how to integrate Centrifugo with NodeJS based backend 78 | * WebRTC chat - shows how to use Centrifugo as WebRTC signaling server to create peer-to-peer communication. 79 | * insecure – example shows how to use Centrifugo running in insecure mode without any web application backend. 80 | * jsfiddle – [simplified chat example on jsfiddle](http://jsfiddle.net/FZambia/yG7Uw/) with predefined user ID, timestamp and token which uses Centrifugo [demo](https://centrifugo.herokuapp.com) instance on Heroku 81 | 82 | ### Community-driven Centrifugo related projects: 83 | 84 | * https://github.com/synw/centcli - Terminal client for Centrifugo 85 | * https://github.com/LaraComponents/centrifuge-broadcaster - Broadcast driver for Laravel framework 86 | to communicate with Centrifugo server API. 87 | * https://github.com/synw/django-instant - Websockets for Django 88 | 89 | {% include "SUMMARY.md" %} 90 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Overview](README.md) 4 | * [Real-time server](server/README.md) 5 | * [Install Centrifugo and quick start](server/start.md) 6 | * [Configuration overview](server/configuration.md) 7 | * [Configuration settings](server/settings.md) 8 | * [Channels](server/channels.md) 9 | * [Engines](server/engines.md) 10 | * [Scaling with Redis](server/scaling.md) 11 | * [Server API](server/api.md) 12 | * [Connection check](server/connection_check.md) 13 | * [Tokens and signatures](server/tokens_and_signatures.md) 14 | * [Client Protocol](server/client_protocol.md) 15 | * [Signal handling](server/signals.md) 16 | * [Stats and metrics](server/stats.md) 17 | * [How recover mechanism works](server/recover.md) 18 | * [Advanced configuration](server/advanced_configuration.md) 19 | * [Client libraries](clients/README.md) 20 | * [JavaScript web browser client](clients/javascript.md) 21 | * [Mobile and other clients](clients/mobile_and_other.md) 22 | * [Server API libraries](libraries/README.md) 23 | * [Admin web interface](web/README.md) 24 | * [Mixed topics](mixed/README.md) 25 | * [Message delivery model](mixed/delivery_model.md) 26 | * [Private channels in browser client](mixed/private_channels.md) 27 | * [Insecure modes](mixed/insecure_modes.md) 28 | * [Exclude processing by sender](mixed/exclude_sender.md) 29 | * [Ping messages](mixed/ping.md) 30 | * [Websocket compression](mixed/websocket_compression.md) 31 | * [Deploy](deploy/README.md) 32 | * [RPM and DEB packages](deploy/packages.md) 33 | * [Docker image](deploy/docker.md) 34 | * [Nginx configuration](deploy/nginx.md) 35 | * [TLS certificates](deploy/certificates.md) 36 | * [Redis high availability](deploy/sentinel.md) 37 | * [Tuning operating system](deploy/tuning.md) 38 | * [FAQ](FAQ.md) 39 | -------------------------------------------------------------------------------- /assets/images/intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centrifugal/documentation/3a74e49ed9d44a4c6fe1fab6399e822c79ebdb1d/assets/images/intro.png -------------------------------------------------------------------------------- /assets/images/scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centrifugal/documentation/3a74e49ed9d44a4c6fe1fab6399e822c79ebdb1d/assets/images/scheme.png -------------------------------------------------------------------------------- /assets/images/scheme_redis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centrifugal/documentation/3a74e49ed9d44a4c6fe1fab6399e822c79ebdb1d/assets/images/scheme_redis.png -------------------------------------------------------------------------------- /assets/images/web.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centrifugal/documentation/3a74e49ed9d44a4c6fe1fab6399e822c79ebdb1d/assets/images/web.gif -------------------------------------------------------------------------------- /assets/styles/website.css: -------------------------------------------------------------------------------- 1 | .book .book-summary ul.summary li a, .book .book-summary ul.summary li span { 2 | padding: 2px 10px; 3 | font-size: 0.9em; 4 | } 5 | .book .book-summary ul.summary li i.fa-check { 6 | right: 5px; 7 | top: 7px; 8 | } -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": "2.x.x", 3 | "links": { 4 | "sidebar": { 5 | "Centrifugal on Github": "https://github.com/centrifugal" 6 | } 7 | }, 8 | "styles": { 9 | "website": "assets/styles/website.css" 10 | } 11 | } -------------------------------------------------------------------------------- /clients/README.md: -------------------------------------------------------------------------------- 1 | # Client libraries to communicate with Centrifugo 2 | 3 | This chapter is about client libraries. Centrifugo originally created to work with browser 4 | clients. But Websocket protocol built on top of convenient TCP and is very simple and popular 5 | so it's very easy to find Websocket client for almost every existing language and write 6 | client for Centrifugo on top of it. So we have client libraries to work from iOS and Android 7 | devices. Also we have some clients for other languages. 8 | 9 | If you want to help and create client for other languages or contribute into existing ones – 10 | your help is very appreciated! 11 | 12 | In this section we will look at Javascript client API in detail - it covers almost all aspects 13 | you can come across when working with Centrifugo. In other client implementations you have 14 | similar APIs - see documentation for specific client. 15 | -------------------------------------------------------------------------------- /clients/javascript.md: -------------------------------------------------------------------------------- 1 | Javascript browser client 2 | ========================= 3 | 4 | This is a documentation is for centrifuge-js client < v2.0. It works with Centrifugo v1. **Current version of Centrifugo is v2**. Actual centrifuge-js client docs located [in its repo README](https://github.com/centrifugal/centrifuge-js). 5 | 6 | At this moment you know how Centrifugo server implemented and how it works. It's time to 7 | connect your web application users to the server from web browser. 8 | 9 | For this purpose javascript client with simple API exists. 10 | 11 | * [Install and quick start](#install-and-quick-start) 12 | * [Connection parameters](#connection-parameters) 13 | * [Configuration parameters](#configuration-parameters) 14 | * [Client API](#client-api) 15 | * [Private channels](#private-channels) 16 | * [Connection check](#connection-check) 17 | 18 | The source code of javascript client located in [repo on Github](https://github.com/centrifugal/centrifuge-js). 19 | 20 | Javascript client can connect to the server in two ways: using pure Websockets or using 21 | [SockJS](https://github.com/sockjs/sockjs-client) library to be able to use various 22 | available fallback transports if client browser does not support Websockets. 23 | 24 | With javascript client you can: 25 | 26 | * connect your user to real-time server 27 | * subscribe on channel and listen to all new messages published into this channel 28 | * get presence information for channel (all clients currently subscribed on channel) 29 | * get history messages for channel 30 | * receive join/leave events for channels (when someone subscribes on channel or 31 | unsubscribes from it) 32 | * publish new messages into channels 33 | 34 | *Note, that in order to use presence, history, join/leave and publish – corresponding options 35 | must be enabled in Centrifugo channel configuration (on top level or for channel namespace).* 36 | 37 | If you are searching for old API docs (`centrifuge-js` < 1.3.0) - [you can find it here](https://github.com/centrifugal/documentation/tree/c69ca51f21c028a6b9bd582afdbf0a5c13331957/client) 38 | 39 | 40 | ## Install and quick start 41 | 42 | The simplest way to use javascript client is including it into your web page using `script` tag: 43 | 44 | ```html 45 | 46 | ``` 47 | 48 | Download client its [from repository](https://github.com/centrifugal/centrifuge-js). 49 | 50 | Browser client is also available via `npm` and `bower`. So you can use: 51 | 52 | ```bash 53 | npm install centrifuge 54 | ``` 55 | 56 | Or: 57 | 58 | ```bash 59 | bower install centrifuge 60 | ``` 61 | 62 | If you want to use SockJS you must also import SockJS client before centrifuge.js 63 | 64 | ```html 65 | 66 | 67 | ``` 68 | 69 | **If you want to support Internet Explorer < 8** then you also need to include JSON 70 | polyfill library: 71 | 72 | ```html 73 | 74 | 75 | 76 | ``` 77 | 78 | As soon as you included all libraries you can create new `Centrifuge` object instance, 79 | subscribe on channel and call `.connect()` method to make actual connection to 80 | Centrifugo server: 81 | 82 | ```javascript 83 | 99 | ``` 100 | 101 | In example above we initialize `Centrifuge` object instance, subscribe on channel 102 | `news`, print all new messages received from channel `news` into console and actually 103 | make connection to Centrifugo. And that's all code which required for simple real-time 104 | messaging handling on client side! 105 | 106 | ***`Centrifuge` object is an instance of [EventEmitter](https://github.com/Olical/EventEmitter/blob/master/docs/api.md).*** 107 | 108 | Parameters `url`, `user`, `timestamp` and `token` are required. Let's look at these 109 | connection parameters and other configuration options in detail. 110 | 111 | ## Connection parameters 112 | 113 | As we showed above to initialize `Centrifuge` object you must provide connection 114 | parameters: `url`, `user`, `timestamp`, `token`, optional `info`. 115 | 116 | **Note that all connection parameters (except url maybe) must come to your Javascript code from 117 | your application backend**. You can render template with these connection parameters, or you can 118 | pass them in cookie, or even make an AJAX GET request from your Javascript code to get `user`, 119 | `timestamp`, `info` and `token`. 120 | 121 | Let's see for what each option is responsible for. 122 | 123 | #### url (required) 124 | 125 | `url` – is an endpoint of Centrifugo server to connect to. 126 | 127 | If your Centrifugo server sits on domain `centrifugo.example.com` then: 128 | 129 | * SockJS endpoint will be `http://centrifugo.example.com/connection` 130 | * Pure Websocket endpoint will be `ws://centrifugo.example.com/connection/websocket` 131 | 132 | Remember to include SockJS library on your page when you want to use SockJS. 133 | 134 | If your Centrifugo works through SSL (**this is recommended btw**) then endpoint 135 | addresses must start with `https` (SockJS) and `wss` (Websocket) instead of `http` 136 | and `ws`. 137 | 138 | You can also set `url` to just `http://centrifugo.example.com` and javascript client will 139 | detect which endpoint to use (SockJS or Websocket) automatically based on SockJS library availability. 140 | 141 | #### user (required) 142 | 143 | `user` **string** is your web application's current user ID. **It can be empty if you don't 144 | have logged in users but in this case you must enable** `anonymous` access option for 145 | channels in Centrifugo configuration (setting `anonymous: true` on top level or for channel 146 | namespace). 147 | 148 | Note, that **it must be string type** even if your application uses numbers as user ID. 149 | Just convert that user ID number to string. 150 | 151 | #### timestamp (required) 152 | 153 | `timestamp` string is UNIX server time in seconds when connection token (see below) 154 | was generated. 155 | 156 | Note, that most programming languages by default return UNIX timestamp as float value. 157 | Or with microseconds included. Centrifugo server **expects only timestamp seconds 158 | represented as string**. For example for Python to get timestamp in a correct format 159 | use `"%.0f" % time.time()` (or just `str(int(time.time()))`) so the result be something 160 | like `"1451991486"`. 161 | 162 | #### token (required) 163 | 164 | `token` is a digest string generated by your web application backend based on Centrifugo 165 | `secret` key, `user` ID, `timestamp` (and optional `info` - see below). 166 | 167 | To create token HMAC SHA-256 algorithm is used. To understand how to generate client 168 | connection token see special chapter [Tokens and signatures](../server/tokens_and_signatures.md). 169 | 170 | **For Python, Ruby, NodeJS, Go and PHP we already have functions to generate client 171 | token in API libraries**. 172 | 173 | Correct token guarantees that connection request to Centrifugo contains valid information 174 | about user ID and timestamp. Token is similar to HTTP cookie, client must never show it 175 | to anyone else. Also remember that you should consider *using private channels* when working 176 | with confidential data. 177 | 178 | #### info (optional) 179 | 180 | You can optionally provide extra parameter `info` when connecting to Centrifugo, i.e.: 181 | 182 | ```javascript 183 | var centrifuge = new Centrifuge({ 184 | url: 'http://centrifuge.example.com/connection', 185 | user: 'USER ID', 186 | timestamp: 'UNIX TIMESTAMP', 187 | info: '{"first_name": "Alexandr", "last_name": "Emelin"}', 188 | token: 'TOKEN' 189 | }); 190 | ``` 191 | 192 | `info` is an additional information about connection. It must be **valid encoded JSON string**. 193 | But to prevent client sending wrong `info` **this JSON string must be used while generating 194 | token**. 195 | 196 | If you don't want to use `info` - you can just omit this parameter while connecting to Centrifugo. 197 | But if you omit it then make sure that info string have not been used in token generation 198 | (i.e. `info` must be empty string). 199 | 200 | ## Configuration parameters 201 | 202 | Let's also look at optional configuration parameters available when initializing 203 | `Centrifuge` object instance. 204 | 205 | #### transports 206 | 207 | In case of using SockJS additional configuration parameter can be used - `transports`. 208 | 209 | It defines allowed SockJS transports and by default equals 210 | 211 | ```javascript 212 | var centrifuge = new Centrifuge({ 213 | ... 214 | transports: [ 215 | 'websocket', 'xdr-streaming', 'xhr-streaming', 216 | 'eventsource', 'iframe-eventsource', 'iframe-htmlfile', 217 | 'xdr-polling', 'xhr-polling', 'iframe-xhr-polling', 'jsonp-polling' 218 | ] 219 | }); 220 | ``` 221 | 222 | i.e. all possible SockJS transports. 223 | 224 | So to say `centrifuge-js` to use only `websocket` and `xhr-streaming` transports when 225 | using SockJS endpoint: 226 | 227 | ```javascript 228 | var centrifuge = new Centrifuge({ 229 | url: 'http://centrifuge.example.com/connection', 230 | user: 'USER ID', 231 | timestamp: 'UNIX TIMESTAMP', 232 | info: '{"first_name": "Alexandr", "last_name": "Emelin"}', 233 | token: 'TOKEN', 234 | transports: ["websocket", "xhr-streaming"] 235 | }); 236 | ``` 237 | 238 | #### sockJS 239 | 240 | **new in 1.3.7**. `sockJS` option allows to explicitly provide SockJS client object to Centrifuge client. 241 | 242 | For example this can be useful if you develop in ES6 using imports. 243 | 244 | ```javascript 245 | import Centrifuge from 'centrifuge' 246 | import SockJS from 'sockjs-client' 247 | 248 | var centrifuge = new Centrifuge({ 249 | url: 'http://centrifuge.example.com/connection', 250 | user: 'USER ID', 251 | timestamp: 'UNIX TIMESTAMP', 252 | info: '{"first_name": "Alexandr", "last_name": "Emelin"}', 253 | token: 'TOKEN', 254 | sockJS: SockJS 255 | }); 256 | ``` 257 | 258 | #### debug 259 | 260 | `debug` is a boolean option which is `false` by default. When enabled lots of various debug 261 | messages will be logged into javascript console. Mostly useful for development or 262 | troubleshooting. 263 | 264 | #### insecure 265 | 266 | `insecure` is a boolean option which is `false` by default. When enabled client will connect 267 | to server in insecure mode - read about this mode in [special docs chapter](../mixed/insecure_modes.md). 268 | 269 | This option nice if you want to use Centrifugo for quick real-time ideas prototyping, demos as 270 | it allows to connect to Centrifugo without `token`, `timestamp` and `user`. And moreover without 271 | application backend! Please, [read separate chapter about insecure modes](../mixed/insecure_modes.md). 272 | 273 | #### retry 274 | 275 | When client disconnected from server it will automatically try to reconnect using exponential 276 | backoff algorithm to get interval between reconnect attempts which value grows exponentially. 277 | `retry` option sets minimal interval value in milliseconds. Default is `1000` milliseconds. 278 | 279 | #### maxRetry 280 | 281 | `maxRetry` sets upper interval value limit when reconnecting. Or your clients will never reconnect 282 | as exponent grows very fast:) Default is `20000` milliseconds. 283 | 284 | #### resubscribe 285 | 286 | `resubscribe` is boolean option that allows to disable automatic resubscribing on 287 | subscriptions. By default it's `true` - i.e. you don't need to manually handle 288 | subscriptions resubscribing and no need to wait `connect` event triggered (first 289 | time or when reconnecting) to start subscribing. `centrifuge-js` will by default 290 | resubscribe automatically when connection established. 291 | 292 | #### server 293 | 294 | `server` is SockJS specific option to set server name into connection urls instead 295 | of random chars. See SockJS docs for more info. 296 | 297 | #### authEndpoint 298 | 299 | `authEndpoint` is url to use when sending auth request for authorizing subscription 300 | on private channel. By default `/centrifuge/auth/`. See also useful related options: 301 | 302 | * `authHeaders` - map of headers to send with auth request (default `{}``) 303 | * `authParams` - map of params to include in auth url (default `{}`) 304 | * `authTransport` - transport to use for auth request (default `ajax`, possible value `jsonp`) 305 | 306 | #### refreshEndpoint 307 | 308 | `refreshEndpoint` is url to use when refreshing client connection parameters when 309 | connection check mechanism enabled in Centrifugo configuration. See also related 310 | options: 311 | 312 | * `refreshHeaders` - map of headers to send with refresh request (default `{}``) 313 | * `refreshParams` - map of params to include in refresh url (default `{}`) 314 | * `refreshTransport` - transport to use for refresh request (default `ajax`, possible value `jsonp`) 315 | * `refreshData` - send extra data in body (as JSON payload) when sending AJAX POST refresh request. 316 | * `refreshAttempts` - limit amount of refresh requests before giving up (by default `null` - unlimited) 317 | * `refreshFailed` - callback function called when `refreshAttempts` came to the end. By default `null` - i.e. nothing called. 318 | 319 | ## Client API 320 | 321 | When `Centrifuge` object properly initialized then it is ready to start communicating 322 | with server. 323 | 324 | #### connect method 325 | 326 | As we showed before, we must call `connect()` method to make an actual connection 327 | request to Centrifugo server: 328 | 329 | ```javascript 330 | var centrifuge = new Centrifuge({ 331 | // ... 332 | }); 333 | 334 | centrifuge.connect(); 335 | ``` 336 | 337 | `connect()` calls actual connection request to server with connection parameters and 338 | configuration options you provided during initialization. 339 | 340 | #### connect event 341 | 342 | After connection will be established and client credentials you provided authorized 343 | then `connect` event on `Centrifuge` object instance will be called. 344 | 345 | You can listen to this setting event listener function on `connect` event: 346 | 347 | ```javascript 348 | centrifuge.on('connect', function(context) { 349 | // now client connected to Centrifugo and authorized 350 | }); 351 | ``` 352 | 353 | What's in `context`: 354 | 355 | ```javascript 356 | { 357 | client: "79ec54fa-8348-4671-650b-d299c193a8a3", 358 | transport: "raw-websocket", 359 | latency: 21 360 | } 361 | ``` 362 | 363 | * `client` – client ID Centrifugo gave to this connection (string) 364 | * `transport` – name of transport used to establish connection with server (string) 365 | * `latency` – latency in milliseconds (int). This measures time passed between sending 366 | `connect` client protocol command and receiving connect response. New in 1.3.1 367 | 368 | #### disconnect event 369 | 370 | `disconnect` event fired on centrifuge object every time client disconnects for 371 | some reason. This can be network disconnect or disconnect initiated by Centrifugo server. 372 | 373 | ```javascript 374 | centrifuge.on('disconnect', function(context) { 375 | // do whatever you need in case of disconnect from server 376 | }); 377 | ``` 378 | 379 | What's in `context`? 380 | 381 | ```javascript 382 | { 383 | reason: "connection closed", 384 | reconnect: true 385 | } 386 | ``` 387 | 388 | * `reason` – the reason of client's disconnect (string) 389 | * `reconnect` – flag indicating if client will reconnect or not (boolean) 390 | 391 | 392 | #### error event 393 | 394 | `error` event called every time on centrifuge object when response with error received. 395 | In normal workflow it will never be happen. But it's better to log these errors to detect 396 | where problem with connection is. 397 | 398 | This event handler is a general error messages sink - it will receives all messages received 399 | from Centrifugo containing error so it could also receive message resulting in `error` event 400 | for subscription (see below). The difference is that this event handler exists mostly for 401 | logging purposes to help developer fix possible problems - while other errors (subscription 402 | error or `publish`, `presence`, `history` call errors) can be theoretically handled to retry 403 | call or resubscribe maybe. 404 | 405 | ```javascript 406 | centrifuge.on('error', function(error) { 407 | // handle error in a way you want, here we just log it into browser console. 408 | console.log(error) 409 | }); 410 | ``` 411 | 412 | What's in `error`? 413 | 414 | ```javascript 415 | { 416 | "message": { 417 | "method": "METHOD", 418 | "error": "ERROR DESCRIPTION", 419 | "advice": "OPTIONAL ERROR ADVICE" 420 | } 421 | } 422 | ``` 423 | 424 | `message` – message from server containing error. It's a raw protocol message resulted in 425 | error event because it contains `error` field. At bare minimum it's recommended to log these 426 | errors. In normal workflow such errors should never exist and must be a signal for developer 427 | that something goes wrong. 428 | 429 | 430 | #### disconnect method 431 | 432 | In some cases you may need to disconnect your client from server, use `disconnect` method to 433 | do this: 434 | 435 | ```javascript 436 | centrifuge.disconnect(); 437 | ``` 438 | 439 | After calling this client will not try to reestablish connection periodically. You must call 440 | `connect` method manually again. 441 | 442 | 443 | ## Subscriptions 444 | 445 | Of course being just connected is useless. What we usually want from Centrifugo is to 446 | receive new messages published into channels. So our next step is `subscribe` on channel 447 | from which we want to receive real-time messages. 448 | 449 | 450 | ### subscribe method 451 | 452 | To subscribe on channel we must use `subscribe` method of `Centrifuge` object instance. 453 | 454 | The simplest usage that allow to subscribe on channel and listen to new messages is: 455 | 456 | ```javascript 457 | var subscription = centrifuge.subscribe("news", function(message) { 458 | // handle new message coming from channel "news" 459 | console.log(message); 460 | }); 461 | ``` 462 | 463 | And that's all! For lots of cases it's enough! But let's look at possible events that 464 | can happen with subscription: 465 | 466 | * `message` – called when new message received (callback function in our previous example is `message` 467 | event callback btw) 468 | * `join` – called when someone joined channel 469 | * `leave` – called when someone left channel 470 | * `subscribe` – called when subscription on channel successful and acknowledged by Centrifugo 471 | server. It can be called several times during javascript code lifetime as browser client 472 | automatically resubscribes on channels after successful reconnect (caused by temporary 473 | network disconnect for example or Centrifugo server restart). 474 | * `error` – called when subscription on channel failed with error. It can called several times 475 | during javascript code lifetime as browser client automatically resubscribes on channels 476 | after successful reconnect (caused by temporary network disconnect for example or Centrifugo 477 | server restart). 478 | * `unsubscribe` – called every time subscription that was successfully subscribed 479 | unsubscribes from channel (can be caused by network disconnect or by calling 480 | `unsubscribe` method of subscription object) 481 | 482 | Don't be frightened by amount of events available. In most cases you only need some of them 483 | until you need full control to what happens with your subscriptions. We will look at format 484 | of messages for this event callbacks later below. 485 | 486 | There are 2 ways setting callback functions for events above. 487 | 488 | First is providing object containing event callbacks as second argument to `subscribe` method. 489 | 490 | ```javascript 491 | var callbacks = { 492 | "message": function(message) { 493 | // See below description of message format 494 | console.log(message); 495 | }, 496 | "join": function(message) { 497 | // See below description of join message format 498 | console.log(message); 499 | }, 500 | "leave": function(message) { 501 | // See below description of leave message format 502 | console.log(message); 503 | }, 504 | "subscribe": function(context) { 505 | // See below description of subscribe callback context format 506 | console.log(context); 507 | }, 508 | "error": function(errContext) { 509 | // See below description of subscribe error callback context format 510 | console.log(err); 511 | }, 512 | "unsubscribe": function(context) { 513 | // See below description of unsubscribe event callback context format 514 | console.log(context); 515 | } 516 | } 517 | 518 | var subscription = centrifuge.subscribe("news", callbacks); 519 | ``` 520 | 521 | Another way is setting callbacks using `on` method of subscription. Subscription object 522 | is event emitter so you can simply do the following: 523 | 524 | ```javascript 525 | var subscription = centrifuge.subscribe("news"); 526 | 527 | subscription.on("message", messageHandlerFunction); 528 | subscription.on("subscribe", subscribeHandlerFunction); 529 | subscription.on("error", subscribeErrorHandlerFunction); 530 | ``` 531 | 532 | ***Subscription objects are instances of [EventEmitter](https://github.com/Olical/EventEmitter/blob/master/docs/api.md).*** 533 | 534 | 535 | ### join and leave events of subscription 536 | 537 | As you know you can enable `join_leave` option for channel in Centrifugo configuration. 538 | This gives you an opportunity to listen to `join` and `leave` events in those channels. 539 | Just set event handlers on `join` and `leave` events of subscription. 540 | 541 | ```javascript 542 | var subscription = centrifuge.subscribe("news", function(message) { 543 | // handle message 544 | }).on("join", function(message) { 545 | console.log("Client joined channel"); 546 | }).on("leave", function(message) { 547 | console.log("Client left channel"); 548 | }); 549 | ``` 550 | 551 | 552 | ### subscription event context formats 553 | 554 | We already know how to listen for events on subscription. Let's look at format of 555 | messages event callback functions receive as arguments. 556 | 557 | #### format of message event context 558 | 559 | Let's look at message format of new message received from channel: 560 | 561 | ```javascript 562 | { 563 | "uid":"6778c79f-ccb2-4a1b-5768-2e7381bc5410", 564 | "channel":"$public:chat", 565 | "data":{"input":"hello"}, 566 | } 567 | ``` 568 | 569 | I.e. `data` field contains actual data that was published. 570 | 571 | Message can optionally contain `client` field (client ID that published message) - if 572 | it was provided when publishing new message: 573 | 574 | ```javascript 575 | { 576 | "uid":"6778c79f-ccb2-4a1b-5768-2e7381bc5410", 577 | "channel":"$public:chat", 578 | "data":{"input":"hello"}, 579 | "client":"7080fd2a-bd69-4f1f-6648-5f3ceba4b643" 580 | } 581 | ``` 582 | 583 | And it can optionally contain additional client `info` in case when this message was 584 | published by javascript client directly using `publish` method (see details below): 585 | 586 | ```javascript 587 | { 588 | "uid":"6778c79f-ccb2-4a1b-5768-2e7381bc5410", 589 | "info":{ 590 | "user":"2694", 591 | "client":"7080fd2a-bd69-4f1f-6648-5f3ceba4b643", 592 | "default_info":{"name":"Alexandr"}, 593 | "channel_info":{"extra":"extra JSON data when authorizing private channel"} 594 | }, 595 | "channel":"$public:chat", 596 | "data":{"input":"hello"}, 597 | "client":"7080fd2a-bd69-4f1f-6648-5f3ceba4b643" 598 | } 599 | ``` 600 | 601 | 602 | #### format of join/leave event message 603 | 604 | I.e. `on("join", function(message) {...})` or `on("leave", function(message) {...})` 605 | 606 | ```javascript 607 | { 608 | "channel":"$public:chat", 609 | "data":{ 610 | "user":"2694", 611 | "client":"2724adea-6e9b-460b-4430-a9f999e94c36", 612 | "default_info":{"first_name":"Alexandr"}, 613 | "channel_info":{"extra":"extra JSON data when authorizing"} 614 | } 615 | } 616 | ``` 617 | 618 | `default_info` and `channel_info` exist in message only if not empty. 619 | 620 | 621 | #### format of subscribe event context 622 | 623 | I.e. `on("subscribe", function(context) {...})` 624 | 625 | ```javascript 626 | { 627 | "channel": "$public:chat", 628 | "isResubscribe": true 629 | } 630 | ``` 631 | 632 | `isResubscribe` – flag showing if this was initial subscribe (`false`) or resubscribe (`true`) 633 | 634 | 635 | #### format of subscription error event context 636 | 637 | I.e. `on("error", function(err) {...})` 638 | 639 | ```javascript 640 | { 641 | "error": "permission denied", 642 | "advice": "fix", 643 | "channel": "$public:chat", 644 | "isResubscribe": true 645 | } 646 | ``` 647 | 648 | `error` - error description 649 | `advice` - optional advice (`retry` or `fix` at moment) 650 | `isResubscribe` – flag showing if this was initial subscribe (`false`) or resubscribe (`true`) 651 | 652 | 653 | #### format of unsubscribe event context 654 | 655 | I.e `on("unsubscribe", function(context) {...})` 656 | 657 | ``` 658 | { 659 | "channel": "$public:chat" 660 | } 661 | ``` 662 | 663 | I.e. it contains only `channel` at moment. 664 | 665 | 666 | ### presence method of subscription 667 | 668 | `presence` allows to get information about clients which are subscribed on channel at 669 | this moment. Note that this information is only available if `presence` option enabled 670 | in Centrifugo configuration for all channels or for channel namespace. 671 | 672 | ```javascript 673 | var subscription = centrifuge.subscribe("news", function(message) { 674 | // handle message 675 | }); 676 | 677 | subscription.presence().then(function(message) { 678 | // presence data received 679 | }, function(err) { 680 | // presence call failed with error 681 | }); 682 | ``` 683 | 684 | `presence` is internally a promise that will be resolved with data or error only 685 | when subscription actually subscribed. 686 | 687 | Format of success callback `message`: 688 | 689 | ```javascript 690 | { 691 | "channel":"$public:chat", 692 | "data":{ 693 | "2724adea-6e9b-460b-4430-a9f999e94c36": { 694 | "user":"2694", 695 | "client":"2724adea-6e9b-460b-4430-a9f999e94c36", 696 | "default_info":{"first_name":"Alexandr"} 697 | }, 698 | "d274505c-ce63-4e24-77cf-971fd8a59f00":{ 699 | "user":"2694", 700 | "client":"d274505c-ce63-4e24-77cf-971fd8a59f00", 701 | "default_info":{"first_name":"Alexandr"} 702 | } 703 | } 704 | } 705 | ``` 706 | 707 | As you can see presence data is a map where keys are client IDs and values are objects 708 | with client information. 709 | 710 | Format of `err` in error callback: 711 | 712 | ```javascript 713 | { 714 | "error": "timeout", 715 | "advice": "retry" 716 | } 717 | ``` 718 | 719 | * `error` – error description (string) 720 | * `advice` – error advice (string, "fix" or "retry" at moment) 721 | 722 | 723 | ### history method of subscription 724 | 725 | `history` method allows to get last messages published into channel. Note that history 726 | for channel must be configured in Centrifugo to be available for `history` calls from 727 | client. 728 | 729 | ```javascript 730 | var subscription = centrifuge.subscribe("news", function(message) { 731 | // handle message 732 | }); 733 | 734 | subscription.history().then(function(message) { 735 | // history messages received 736 | }, function(err) { 737 | // history call failed with error 738 | }); 739 | }); 740 | ``` 741 | 742 | Success callback `message` format: 743 | 744 | ```javascript 745 | { 746 | "channel": "$public:chat", 747 | "data": [ 748 | { 749 | "uid": "87219102-a31d-44ed-489d-52b1a7fa520c", 750 | "channel": "$public:chat", 751 | "data": {"input": "hello2"} 752 | }, 753 | { 754 | "uid": "71617557-7466-4cbb-760e-639042a5cade", 755 | "channel": "$public:chat", 756 | "data": {"input": "hello1"} 757 | } 758 | ] 759 | } 760 | ``` 761 | 762 | Where `data` is an array of messages published into channel. 763 | 764 | Note that also additional fields can be included in messages - `client`, `info` if those 765 | fields were in original messages. 766 | 767 | `err` format – the same as for `presence` method. 768 | 769 | 770 | ### publish method of subscription 771 | 772 | `publish` method of subscription object allows to publish data into channel directly 773 | from client. The main idea of Centrifugo is server side only push. Usually your application 774 | backend receives new event (for example new comment created, someone clicked like button 775 | etc) and then backend posts that event into Centrifugo over API. But in some cases you may 776 | need to allow clients to publish data into channels themselves. This can be used for demo 777 | projects, when prototyping ideas for example, for personal usage. And this allow to make 778 | something with real-time features without any application backend at all. Just javascript 779 | code and Centrifugo. 780 | 781 | **So to emphasize: using client publish is not an idiomatic Centrifugo usage. It's not for 782 | production applications but in some cases (demos, personal usage, Centrifugo as backend 783 | microservice) can be justified and convenient. In most real-life apps you need to send new 784 | data to your application backend first (using the convenient way, for example AJAX request 785 | in web app) and then publish data to Centrifugo over Centrifugo API.** 786 | 787 | To do this you can use `publish` method. Note that just like presence and history publish 788 | must be allowed in Centrifugo configuration for all channels or for channel namespace. When 789 | using `publish` data will go through Centrifugo to all clients in channel. Your application 790 | backend won't receive this message. 791 | 792 | ```javascript 793 | var subscription = centrifuge.subscribe("news", function(message) { 794 | // handle message 795 | }); 796 | 797 | subscription.publish({"input": "hello world"}).then(function() { 798 | // success ack from Centrifugo received 799 | }, function(err) { 800 | // publish call failed with error 801 | }); 802 | }); 803 | ``` 804 | 805 | `err` format – the same as for `presence` method. 806 | 807 | 808 | ### unsubscribe method of subscription 809 | 810 | You can call `unsubscribe` method to unsubscribe from subscription: 811 | 812 | ```javascript 813 | subscription.unsubscribe(); 814 | ``` 815 | 816 | ### subscribe method of subscription 817 | 818 | You can restore subscription after unsubscribing calling `.subscribe()` method: 819 | 820 | ```javascript 821 | subscription.subscribe(); 822 | ``` 823 | 824 | ### ready method of subscription 825 | 826 | A small drawback of setting event handlers on subscription using `on` method is that event 827 | handlers can be set after `subscribe` event of underlying subscription already fired. This 828 | is not a problem in general but can be actual if you use one subscription (i.e. subscription 829 | to the same channel) from different parts of your javascript application - so be careful. 830 | 831 | For this case one extra helper method `.ready(callback, errback)` exists. This method calls 832 | `callback` if subscription already subscribed and calls `errback` if subscription already 833 | failed to subscribe with some error (because you subscribed on this channel before). So 834 | when you want to call subscribe on channel already subscribed before you may find `ready()` 835 | method useful: 836 | 837 | ```javascript 838 | var subscription = centrifuge.subscribe("news", function(message) { 839 | // handle message; 840 | }); 841 | 842 | // artificially model subscription to the same channel that happen after 843 | // first subscription successfully subscribed - subscribe on the same 844 | // channel after 5 seconds. 845 | setTimeout(function() { 846 | var anotherSubscription = centrifuge.subscribe("news", function(message) { 847 | // another listener of channel "news" 848 | }).on("subscribe", function() { 849 | // won't be called on first subscribe because subscription already subscribed! 850 | // but will be called every time automatic resubscribe after network disconnect 851 | // happens 852 | }); 853 | // one of subscribeSuccessHandler (or subscribeErrorHandler) will be called 854 | // only if subscription already subscribed (or subscribe request already failed). 855 | anotherSubscription.ready(subscribeSuccessHandler, subscribeErrorHandler); 856 | }, 5000); 857 | ``` 858 | 859 | When called `callback` and `errback` of `ready` method receive the same arguments as 860 | callback functions for `subscribe` and `error` events of subscription. 861 | 862 | 863 | ### Message batching 864 | 865 | There is also message batching support. It allows to send several messages to server 866 | in one request - this can be especially useful when connection established via one of 867 | SockJS polling transports. 868 | 869 | You can start collecting messages to send calling `startBatching()` method: 870 | 871 | ```javascript 872 | centrifuge.startBatching(); 873 | ``` 874 | 875 | When you want to actually send all collected messages to server call `flush()` method: 876 | 877 | ```javascript 878 | centrifuge.flush(); 879 | ``` 880 | 881 | Finally if you don't want batching anymore call `stopBatching()` method: 882 | 883 | ```javascript 884 | centrifuge.stopBatching(); 885 | ``` 886 | 887 | Call `stopBatching(true)` to flush all messages and stop batching: 888 | 889 | ```javascript 890 | centrifuge.stopBatching(true); 891 | ``` 892 | 893 | 894 | ## Private channels 895 | 896 | If channel name starts with `$` then subscription on this channel will be checked via 897 | AJAX POST request from javascript client to your web application backend. 898 | 899 | You can subscribe on private channel as usual: 900 | 901 | ```javascript 902 | centrifuge.subscribe('$private', function(message) { 903 | // process message 904 | }); 905 | ``` 906 | 907 | But in this case client will first check subscription via your backend sending POST request 908 | to `/centrifuge/auth/` endpoint (by default, can be changed via configuration option 909 | `authEndpoint`). This request will contain `client` parameter which is your connection 910 | client ID and `channels` parameter - one or multiple private channels client wants to 911 | subscribe to. Your server should validate all this subscriptions and return properly 912 | signed responses. 913 | 914 | There are also two public API methods which can help to subscribe to many private 915 | channels sending only one POST request to your web application backend: `startAuthBatching` 916 | and `stopAuthBatching`. When you `startAuthBatching` javascript client will collect 917 | private subscriptions until `stopAuthBatching()` called – and then send them all at 918 | once. 919 | 920 | Read more about private channels in [special documentation chapter](../mixed/private_channels.md). 921 | 922 | 923 | ## Connection check 924 | 925 | Javascript client has support for refreshing connection when `connection_lifetime` option 926 | set in Centrifugo. See more details in [dedicated chapter](../server/connection_check.md). 927 | -------------------------------------------------------------------------------- /clients/mobile_and_other.md: -------------------------------------------------------------------------------- 1 | # Mobile and other clients 2 | 3 | Let's look at available Websocket clients for Centrifugo. 4 | 5 | ### Native Android client library 6 | 7 | [Android client](https://github.com/centrifugal/centrifuge-android) contributed by [Semyon Danilov](https://github.com/SammyVimes). 8 | 9 | There is example in repo which can help to start. 10 | 11 | ### Native iOS client library 12 | 13 | [iOS client](https://github.com/centrifugal/centrifuge-ios) contributed by [Herman Saprykin](https://github.com/mogol). 14 | 15 | There is [example](https://github.com/centrifugal/centrifuge-ios/tree/develop/Example) in repo which can help to start. 16 | 17 | ### iOS and Android client library using Gomobile project 18 | 19 | As iOS and Android clients supported by community members they unfortunately lack some 20 | features. Your help is really appreciated to make native mobile clients better. 21 | 22 | As option we have full-featured [centrifuge-mobile](https://github.com/centrifugal/centrifuge-mobile) client for mobile platforms built on top of [gomobile]() project. 23 | 24 | Gomobile is experimental technology so this library considered experimental too. See it's readme for more information. 25 | 26 | ### Go client 27 | 28 | Go client library located [on Github](https://github.com/centrifugal/centrifuge-go). 29 | 30 | There are some examples in repo which can help you to start. Note that you can also use `centrifuge-mobile` from Go code.` 31 | 32 | ### Java Client 33 | 34 | [Java Client](https://github.com/donald-jackson/centrifuge-java) contributed by [Donald Jackson](https://github.com/donald-jackson). 35 | 36 | There are some examples in the README. It is a fork of the Android client written by [Semyon Danilov](https://github.com/SammyVimes). 37 | 38 | ### Python Asyncio client 39 | 40 | [Centrifuge-python](https://github.com/centrifugal/centrifuge-python) client is a work in progress - there was no PYPI release yet. But it already works pretty well with Centrifugo. 41 | 42 | ### Perl client 43 | 44 | Benoît Chauvet created a repo with a [Perl client](https://github.com/Orabig/centrifugo-perl-client) on top of Anyevent - it's a work in progress at moment but still can be very useful. 45 | 46 | 47 | -------------------------------------------------------------------------------- /deploy/README.md: -------------------------------------------------------------------------------- 1 | # How to deploy -------------------------------------------------------------------------------- /deploy/certificates.md: -------------------------------------------------------------------------------- 1 | # TLS certificates 2 | 3 | TLS/SSL layer is very important not only for securing your connections but also to increase a 4 | chance to establish Websocket connection. **In most situations you will put TLS termination task 5 | on your reverse proxy/load balancing software such as Nginx**. 6 | 7 | There are situations though when you want to serve secure connections by Centrifugo itself. 8 | 9 | There are two ways to do this: using TLS certificate `cert` and `key` files that you've got 10 | from your CA provider or using automatic certificate handling via [ACME](https://ietf-wg-acme.github.io/acme/) provider (only 11 | [Let's Encrypt](https://letsencrypt.org/) at this moment). 12 | 13 | ### Using crt and key files 14 | 15 | In first way you already have `cert` and `key` files. For development you can create self-signed 16 | certificate - see [this instruction](https://devcenter.heroku.com/articles/ssl-certificate-self) as 17 | example. 18 | 19 | Then to start Centrifugo use the following command: 20 | 21 | ``` 22 | ./centrifugo --config=config.json --ssl --ssl_key=server.key --ssl_cert=server.crt 23 | ``` 24 | 25 | Or just use configuration file: 26 | 27 | ```json 28 | { 29 | ... 30 | "ssl": true, 31 | "ssl_key": "server.key", 32 | "ssl_cert": "server.crt" 33 | } 34 | ``` 35 | 36 | And run: 37 | 38 | ``` 39 | ./centrifugo --config=config.json 40 | ``` 41 | 42 | ### Automatic certificates 43 | 44 | For automatic certificates from Let's Encrypt add into configuration file: 45 | 46 | ``` 47 | { 48 | ... 49 | "ssl_autocert": true, 50 | "ssl_autocert_host_whitelist": "www.example.com", 51 | "ssl_autocert_cache_dir": "/tmp/certs", 52 | "ssl_autocert_email": "user@example.com" 53 | } 54 | ``` 55 | 56 | `ssl_autocert` says Centrifugo that you want automatic certificate handling using ACME provider. 57 | 58 | `ssl_autocert_host_whitelist` is a string with your app domain address. This can be comma-separated 59 | list. It's optional but recommended for extra security. 60 | 61 | `ssl_autocert_cache_dir` is a path to a folder to cache issued certificate files. This is optional 62 | but will increase performance. 63 | 64 | `ssl_autocert_email` is optional - it's an email address ACME provider will send notifications 65 | about problems with your certificates. 66 | 67 | When configured correctly and your domain is valid (`localhost` will not work) - certificates 68 | will be retrieved on first request to Centrifugo. 69 | 70 | Also Let's Encrypt certificates will be automatically renewed. 71 | 72 | There are tho options (new in v1.6.5) that allow Centrifugo to support TLS client connections from older 73 | browsers such as Chrome 49 on Windows XP and IE8 on XP: 74 | 75 | * `ssl_autocert_force_rsa` - this is a boolean option, by default `false`. When enabled it forces 76 | autocert manager generate certificates with 2048-bit RSA keys. 77 | * `ssl_autocert_server_name` - string option, allows to set server name for client handshake hello. 78 | This can be useful to deal with old browsers without SNI support - see [comment](https://github.com/centrifugal/centrifugo/issues/144#issuecomment-279393819) 79 | -------------------------------------------------------------------------------- /deploy/docker.md: -------------------------------------------------------------------------------- 1 | # Docker image 2 | 3 | Centrifugo server has docker image [available on Docker Hub](https://hub.docker.com/r/centrifugo/centrifugo/). 4 | 5 | ``` 6 | docker pull centrifugo/centrifugo 7 | ``` 8 | 9 | Run: 10 | 11 | ```bash 12 | docker run --ulimit nofile=65536:65536 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo centrifugo -c config.json 13 | ``` 14 | 15 | Note that docker allows to set nofile limits in command-line arguments. 16 | 17 | To run with admin web interface: 18 | 19 | ```bash 20 | docker run --ulimit nofile=65536:65536 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo centrifugo -c config.json --web 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /deploy/nginx.md: -------------------------------------------------------------------------------- 1 | # Nginx configuration 2 | 3 | Although it's possible to use Centrifugo without any reverse proxy before it, 4 | it's still a good idea to keep Centrifugo behind mature reverse proxy to deal with 5 | edge cases when handling HTTP/Websocket connections from the wild. Also you probably 6 | want some sort of load balancing eventually between Centrifugo nodes so that proxy 7 | can be such a balancer too. 8 | 9 | In this section we will look at [Nginx](http://nginx.org/) configuration to deploy Centrifugo. 10 | 11 | Minimal Nginx version – **1.3.13** because it was the first version that can proxy 12 | Websocket connections. 13 | 14 | There are 2 ways: running Centrifugo server as separate service on its own 15 | domain or embed it to a location of your web site (for example to `/centrifugo`). 16 | 17 | ### separate domain for Centrifugo 18 | 19 | ``` 20 | upstream centrifugo { 21 | # Enumerate all upstream servers here 22 | #sticky; 23 | ip_hash; 24 | server 127.0.0.1:8000; 25 | #server 127.0.0.1:8001; 26 | } 27 | 28 | map $http_upgrade $connection_upgrade { 29 | default upgrade; 30 | '' close; 31 | } 32 | 33 | #server { 34 | # listen 80; 35 | # server_name centrifugo.example.com; 36 | # rewrite ^(.*) https://$server_name$1 permanent; 37 | #} 38 | 39 | server { 40 | 41 | server_name centrifugo.example.com; 42 | 43 | listen 80; 44 | 45 | #listen 443; 46 | #ssl on; 47 | #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 48 | #ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; 49 | #ssl_certificate /etc/nginx/ssl/wildcard.example.com.crt; 50 | #ssl_certificate_key /etc/nginx/ssl/wildcard.example.com.key; 51 | #ssl_session_cache shared:SSL:10m;ssl_session_timeout 10m; 52 | 53 | include /etc/nginx/mime.types; 54 | default_type application/octet-stream; 55 | 56 | sendfile on; 57 | tcp_nopush on; 58 | tcp_nodelay on; 59 | gzip on; 60 | gzip_min_length 1000; 61 | gzip_proxied any; 62 | 63 | # Only retry if there was a communication error, not a timeout 64 | # on the Tornado server (to avoid propagating "queries of death" 65 | # to all frontends) 66 | proxy_next_upstream error; 67 | 68 | proxy_set_header X-Real-IP $remote_addr; 69 | proxy_set_header X-Scheme $scheme; 70 | proxy_set_header Host $http_host; 71 | 72 | location /connection { 73 | proxy_pass http://centrifugo; 74 | proxy_buffering off; 75 | keepalive_timeout 65; 76 | proxy_read_timeout 60s; 77 | proxy_http_version 1.1; 78 | proxy_set_header X-Real-IP $remote_addr; 79 | proxy_set_header X-Scheme $scheme; 80 | proxy_set_header Host $http_host; 81 | proxy_set_header Upgrade $http_upgrade; 82 | proxy_set_header Connection $connection_upgrade; 83 | } 84 | 85 | location / { 86 | proxy_pass http://centrifugo; 87 | } 88 | 89 | error_page 500 502 503 504 /50x.html; 90 | 91 | location = /50x.html { 92 | root /usr/share/nginx/html; 93 | } 94 | 95 | } 96 | ``` 97 | 98 | If you want to use web interface then you should also add `/socket` location 99 | to handle admin websocket connections: 100 | 101 | ``` 102 | location /socket { 103 | proxy_pass http://centrifugo; 104 | proxy_buffering off; 105 | keepalive_timeout 65; 106 | proxy_read_timeout 60s; 107 | proxy_http_version 1.1; 108 | proxy_set_header X-Real-IP $remote_addr; 109 | proxy_set_header X-Scheme $scheme; 110 | proxy_set_header Host $http_host; 111 | proxy_set_header Upgrade $http_upgrade; 112 | proxy_set_header Connection $connection_upgrade; 113 | } 114 | ``` 115 | 116 | ### embed to a location of web site 117 | 118 | ``` 119 | upstream centrifugo { 120 | # Enumerate all the Tornado servers here 121 | #sticky; 122 | ip_hash; 123 | server 127.0.0.1:8000; 124 | #server 127.0.0.1:8001; 125 | } 126 | 127 | map $http_upgrade $connection_upgrade { 128 | default upgrade; 129 | '' close; 130 | } 131 | 132 | server { 133 | 134 | # ... your web site Nginx config 135 | 136 | location /centrifugo/ { 137 | rewrite ^/centrifugo/(.*) /$1 break; 138 | proxy_pass_header Server; 139 | proxy_set_header Host $http_host; 140 | proxy_redirect off; 141 | proxy_set_header X-Real-IP $remote_addr; 142 | proxy_set_header X-Scheme $scheme; 143 | proxy_pass http://centrifugo; 144 | } 145 | 146 | location /centrifugo/socket { 147 | rewrite ^/centrifugo(.*) $1 break; 148 | 149 | proxy_next_upstream error; 150 | proxy_buffering off; 151 | keepalive_timeout 65; 152 | proxy_pass http://centrifugo; 153 | proxy_read_timeout 60s; 154 | proxy_set_header X-Real-IP $remote_addr; 155 | proxy_set_header X-Scheme $scheme; 156 | proxy_set_header Host $http_host; 157 | proxy_http_version 1.1; 158 | proxy_set_header Upgrade $http_upgrade; 159 | proxy_set_header Connection $connection_upgrade; 160 | } 161 | 162 | location /centrifugo/connection { 163 | rewrite ^/centrifugo(.*) $1 break; 164 | 165 | proxy_next_upstream error; 166 | gzip on; 167 | gzip_min_length 1000; 168 | gzip_proxied any; 169 | proxy_buffering off; 170 | keepalive_timeout 65; 171 | proxy_pass http://centrifugo; 172 | proxy_read_timeout 60s; 173 | proxy_set_header X-Real-IP $remote_addr; 174 | proxy_set_header X-Scheme $scheme; 175 | proxy_set_header Host $http_host; 176 | proxy_http_version 1.1; 177 | proxy_set_header Upgrade $http_upgrade; 178 | proxy_set_header Connection $connection_upgrade; 179 | } 180 | 181 | } 182 | ``` 183 | 184 | ### sticky 185 | 186 | You may be noticed commented `sticky;` directive in nginx upstream configuration. 187 | 188 | When using SockJS and client connects to Centrifugo - SockJS session created - and 189 | to communicate client must send all next requests to the same upstream backend. 190 | 191 | In this configuration we use `ip_hash;` directive to proxy clients with the same ip 192 | address to the same upstream backend. 193 | 194 | But `ip_hash;` is not the best choice in this case, because there could be situations 195 | where a lot of different browsers are coming with the same IP address (behind proxies) 196 | and the load balancing system won't be fair. Also fair load balancing does not work 197 | during development - when all clients connecting from localhost. 198 | 199 | So the best solution would be using something like [nginx-sticky-module](https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/overview) 200 | which uses setting a special cookie to track the upstream server for client. 201 | 202 | ### worker_connections 203 | 204 | You may also need to update `worker_connections` option of Nginx: 205 | 206 | ``` 207 | events { 208 | worker_connections 40000; 209 | } 210 | ``` 211 | 212 | ### upstream keepalive 213 | 214 | See chapter about operating system tuning for more details. 215 | -------------------------------------------------------------------------------- /deploy/packages.md: -------------------------------------------------------------------------------- 1 | # RPM and DEB packages 2 | 3 | Every time we make new Centrifugo release we upload rpm and deb packages for 4 | popular linux distributions on [packagecloud.io](https://packagecloud.io/FZambia/centrifugo). 5 | 6 | Currently we support versions of the following distributions: 7 | 8 | * 64-bit Debian 7 Wheezy 9 | * 64-bit Debian 8 Jessie 10 | * 64-bit Ubuntu 14.04 Trusty 11 | * 64-bit Ubuntu 16.04 Xenial 12 | * 64-bit Centos 7 13 | * 64-bit Centos 6 14 | 15 | See [full list of available packages](https://packagecloud.io/FZambia/centrifugo) and 16 | [installation instructions](https://packagecloud.io/FZambia/centrifugo/install). 17 | 18 | Also note that if your linux distro is not in list you can ask us to package 19 | for it or just download appropriate package from packagecloud that fits your 20 | distribution. 21 | 22 | Centrifugo also works on 32-bit architectures, but we don't support packaging for it 23 | as 64-bit is more convenient for servers today. 24 | -------------------------------------------------------------------------------- /deploy/sentinel.md: -------------------------------------------------------------------------------- 1 | # Redis Sentinel for high availability. 2 | 3 | **New in 1.4.2** 4 | 5 | Centrifugo supports official way to add high availability to Redis - Redis [Sentinel](http://redis.io/topics/sentinel). 6 | 7 | For this you only need to utilize 2 Redis Engine options: `redis_master_name` and `redis_sentinels`. 8 | 9 | `redis_master_name` - is a name of master your Sentinels monitor. 10 | 11 | `redis_sentinels` - comma-separated addresses of Sentinel servers. At least one known server required. 12 | 13 | So you can start Centrifugo which will use Sentinels to discover redis master instance like this: 14 | 15 | ``` 16 | centrifugo --config=config.json --engine=redis --redis_master_name=mymaster --redis_sentinels=":26379" 17 | ``` 18 | 19 | Sentinel configuration files can look like this: 20 | 21 | ``` 22 | port 26379 23 | sentinel monitor mymaster 127.0.0.1 6379 2 24 | sentinel down-after-milliseconds mymaster 10000 25 | sentinel failover-timeout mymaster 60000 26 | ``` 27 | 28 | You can find how to properly setup Sentinels [in official documentation](http://redis.io/topics/sentinel). 29 | 30 | Note that when your redis master instance down there will be small downtime interval until Sentinels 31 | discover a problem and come to quorum decision about new master. The length of this period depends on 32 | Sentinel configuration. 33 | 34 | 35 | -------------------------------------------------------------------------------- /deploy/tuning.md: -------------------------------------------------------------------------------- 1 | # Tuning operating system 2 | 3 | As Centrifugo/Centrifuge deals with lots of persistent connections your operating system must be 4 | ready for it. 5 | 6 | ### open files limit 7 | 8 | First of all you should increase a max number of open files your processes can open. 9 | 10 | To get you current open files limit run: 11 | 12 | ``` 13 | ulimit -n 14 | ``` 15 | 16 | The result shows approximately how many clients your server can handle. 17 | 18 | See http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/ to know how to increase this number. 19 | 20 | If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 32768. 21 | 22 | You may also need to increase max open files for Nginx. 23 | 24 | ### lots of sockets in TIME_WAIT state. 25 | 26 | Look how many socket descriptors in TIME_WAIT state. 27 | 28 | ``` 29 | netstat -an |grep TIME_WAIT | grep CENTRIFUGO_PID | wc -l 30 | ``` 31 | 32 | Under load when lots of connections and disconnection happen lots of used socket descriptors can 33 | stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various 34 | errors when using Centrifugo. For example something like `(99: Cannot assign requested address) 35 | while connecting to upstream` in Nginx error log and 502 on client side. In this case there are 36 | several advices that can help. 37 | 38 | Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html 39 | 40 | There is a perfect article about operating system tuning for lots of connections: https://engineering.gosquared.com/optimising-nginx-node-js-and-networking-for-heavy-workloads. 41 | 42 | To summarize: 43 | 44 | 1. Increase ip_local_port_range 45 | 2. If you are using Nginx set `keepalive` directive in upstream. 46 | 47 | ``` 48 | upstream centrifugo { 49 | #sticky; 50 | ip_hash; 51 | server 127.0.0.1:8000; 52 | keepalive 512; 53 | } 54 | ``` 55 | 56 | 3. And finally if the problem is not gone away consider trying to enable `net.ipv4.tcp_tw_reuse` 57 | -------------------------------------------------------------------------------- /libraries/README.md: -------------------------------------------------------------------------------- 1 | # Server API libraries 2 | 3 | As you could see in Centrifugal overview there are several officially supported libraries 4 | for communicating with server API at moment. 5 | 6 | If you have not found a library for your favorite language – you can go completely 7 | without it. You have an option to publish via Redis Engine or just implement 8 | calls to HTTP API yourself - [see server API description](../server/api.md). 9 | 10 | Also note that [Cent](https://github.com/centrifugal/cent) contains everything you 11 | need to communicate with server API - so if you have questions - just look at 12 | its source code as reference - as it is written in Python it's very easy to read 13 | and understand. And there are few lines of code actually. 14 | 15 | ### Python 16 | 17 | Python HTTP API client located [on Github](https://github.com/centrifugal/cent). 18 | 19 | It can also be used as command-line tool to send various commands to Centrifugo. 20 | 21 | ### PHP 22 | 23 | We have two actual API clients for PHP. 24 | 25 | The first one is HTTP API client created by Dmitriy Soldatenko. You can find it [on Github](https://github.com/sl4mmer/phpcent). 26 | It's simple to use - just follow its README and enjoy communicating with Centrifugo HTTP API. 27 | 28 | Another API client developed by [Oleh Ozimok](https://github.com/oleh-ozimok) - [php-centrifugo](https://github.com/oleh-ozimok/php-centrifugo). 29 | It allows to work with Redis Engine API queue. See more detailed description in library README. 30 | 31 | ### Ruby 32 | 33 | Ruby client located [on Github](https://github.com/centrifugal/rubycent). 34 | 35 | It's very simple to use - just follow its README and enjoy communicating with API. 36 | 37 | 38 | ### NodeJS 39 | 40 | NodeJS client located [on Github](https://github.com/centrifugal/jscent). 41 | 42 | Basic example: 43 | 44 | ```javascript 45 | Client = require("jscent"); 46 | 47 | var c = new Client({url: "http://localhost:8000", secret: "secret"}); 48 | 49 | c.publish("$public:chat", {"input": "test"}, function(err, resp){console.log(err, resp)}); 50 | ``` 51 | 52 | ### Go 53 | 54 | Go HTTP API client located [on Github](https://github.com/centrifugal/gocent). 55 | 56 | 57 | ### Java 58 | 59 | There is third party libraries originally built for Centrifuge - predecessor of Centrifugo. 60 | They are not updated to work with Centrifugo, but can be used as starting point for 61 | your communication code. 62 | 63 | * [API library for Java](https://github.com/mcoetzee/centrifuge-publisher) by Markus Coetzee 64 | 65 | To work with Centrifugo client above must use `sha256` HMAC algorithm instead of `md5` and 66 | do not use project ID (project ID does not exist anymore). 67 | -------------------------------------------------------------------------------- /mixed/README.md: -------------------------------------------------------------------------------- 1 | # Mixed topics 2 | 3 | This section contains topics related to both client and server together. -------------------------------------------------------------------------------- /mixed/delivery_model.md: -------------------------------------------------------------------------------- 1 | # Message delivery model 2 | 3 | The model of message delivery of Centrifugo server is `at most once`. 4 | 5 | This means that message you send to Centrifugo can be theoretically lost while moving towards 6 | your clients. Centrifugo tries to do a best effort to prevent message losses but you should be 7 | aware of this fact. Your application should tolerate this. Centrifugo has an option to 8 | automatically recover messages that have been lost because of short network disconnections. But 9 | there are cases when Centrifugo can't guarantee message delivery. We also recommend to model your 10 | applications in a way that users don't notice when message have been lost. For example if your user 11 | posts a new comment over AJAX call to your application backend - you should not rely only on 12 | Centrifugo to get new comment form and display it - you should return new comment data in AJAX call 13 | response and render it. Be careful to not draw comments twice in this case. 14 | 15 | Message order in channels guaranteed to be the same while you publish messages into channel one after 16 | another or publish them in one request (array of published messages). If you do parallel 17 | publishes into the same channel then Centrifugo can't strongly guarantee message order. 18 | -------------------------------------------------------------------------------- /mixed/exclude_sender.md: -------------------------------------------------------------------------------- 1 | # Exclude message processing by sender. 2 | 3 | In some situations you may want to prevent the client that published a message from processing 4 | it after receiving from channel. The solution for doing this is described here. 5 | 6 | ### new in 0.2.0 7 | 8 | Publish command includes optional `client` field. Include `client` ID (to get client connection 9 | ID call `centrifuge.getClientId()` in javascript client) in publish API request and `client` 10 | will be added on top level of published message. You can compare current client ID in javascript 11 | with client ID in received message and if both values equal then in some situations you will wish 12 | to drop this message as it was published by this client and probably already processed (via 13 | optimistic optimization or after successful AJAX call to web application backend initiated this 14 | message). 15 | 16 | I.e. something like this: 17 | 18 | ```javascript 19 | var subscription = centrifuge.subscribe(channel, function(message) { 20 | if (message.client === centrifuge.getClientId()) { 21 | return; 22 | } 23 | // if clients not equal – process message as usual 24 | }); 25 | ``` 26 | 27 | To include `client` into publish API request you should provide it for your backend 28 | in AJAX data when client creates an event in your application. 29 | -------------------------------------------------------------------------------- /mixed/insecure_modes.md: -------------------------------------------------------------------------------- 1 | # Insecure modes 2 | 3 | You can run Centrifugo in insecure client mode. 4 | 5 | Insecure client mode: 6 | 7 | * disables client timestamp and token check 8 | * allows anonymous access for all channels 9 | * allows client to publish into all channels 10 | * suppresses connection check 11 | 12 | This allows to use Centrifugo and centrifuge javascript client as a quick and simple 13 | solution when making real-time demos, presentations, testing ideas etc. But this mode 14 | is mostly for personal and demonstration uses - you should never turn this mode on 15 | in production until you really want it to be there. 16 | 17 | ### on server side 18 | 19 | To start Centrifugo in this mode use `--insecure` flag: 20 | 21 | ```bash 22 | centrifuge --config=config.json --insecure 23 | ``` 24 | 25 | You can also set `insecure` option in configuration file to do the same. 26 | 27 | ### on client side 28 | 29 | When using insecure mode you can create client connection in this way: 30 | 31 | ```javascript 32 | var centrifuge = new Centrifuge({ 33 | "url": url, 34 | "insecure": true 35 | }); 36 | ``` 37 | 38 | I.e. without `token`, `user` and `timestamp` parameters. So you can connect to 39 | Centrifugo without any backend code. 40 | 41 | Look at [demo](https://github.com/centrifugal/centrifuge/tree/master/examples/insecure_mode) to 42 | see insecure mode in action. 43 | 44 | # Insecure HTTP API mode 45 | 46 | Allows to turn of checking HTTP API request API sign. This can be useful if you don't want 47 | to sign every API request - for example if you closed API endpoint with firewall or you want 48 | to play with API commands from command line using CURL. 49 | 50 | To start Centrifugo in this mode use `--insecure_api` flag: 51 | 52 | ```bash 53 | centrifugo --config=config.json --insecure_api 54 | ``` 55 | 56 | # Insecure admin mode (new in v1.3.0, changed in v1.6.0) 57 | 58 | Allows run Centrifugo in insecure admin mode - in this case you don't need to set `admin_password` and `admin_secret` 59 | for admin endpoints in config - all admin endpoints access won't require authentication at all. 60 | 61 | Note that this is **only for development or if you protected web interface with firewall rules in production**. 62 | 63 | To start Centrifugo in this insecure mode run: 64 | 65 | ```bash 66 | centrifugo --config=config.json --insecure_admin 67 | ``` 68 | 69 | If this mode enabled and you are using web interface **you will be logged in automatically without any password**. This 70 | can be useful if you want to hide Centrifugo web interface behind you own company authentication proxy and don't want to 71 | have extra password for Centrifugo: 72 | 73 | ```bash 74 | centrifugo --config=config.json --insecure_admin --web 75 | ``` 76 | 77 | Again: every insecure mode described here potentially dangerous and you must understand how to protect your Centrifugo 78 | by firewall rules this before turning on insecure modes in production. 79 | -------------------------------------------------------------------------------- /mixed/ping.md: -------------------------------------------------------------------------------- 1 | # Ping messages 2 | 3 | This document describes how Centrifugo works with client-server pings. 4 | 5 | In internet environment ping messages required for keeping long-living connections 6 | alive. Centrifugo is a server that works with such connections - so ping messages is 7 | what we pay a lot of attention to. 8 | 9 | Starting from Centrifugo v1.6.0 `centrifuge-js` sends periodic ping messages to Centrifugo 10 | automatically. It works in a manner that ping messages only sent when connection is idle - i.e. 11 | there was no activity for some time (by default 25 seconds). This means that ping messages do 12 | not introduce significant overhead for busy applications. 13 | 14 | Javascript client allows you to disable automatic ping messages - see its documentation. 15 | 16 | Client to server pings allow to detect problems with connection and disconnect in case of no 17 | response from server received in reasonable time. 18 | 19 | Centrifugo also sends server to client pings every 25 seconds. In case of raw websocket it's 20 | a special PING websocket frame, in case of SockJS it's an `h` frame. Server to client pings 21 | allow to close inactive connections. 22 | 23 | In short - if you use default settings - you are ok and don't need to worry. 24 | -------------------------------------------------------------------------------- /mixed/private_channels.md: -------------------------------------------------------------------------------- 1 | # Private channels in browser client 2 | 3 | All channels starting with `$` considered private. Subscribing on private channel in 4 | javascript browser client does not differ from subscribing on usual channels. But you should 5 | implement an endpoint in your web application that will check if current user can subscribe 6 | on certain private channels. 7 | 8 | By default javascript client will send AJAX POST request to `/centrifuge/auth/` url. You 9 | can change this address and add additional request headers via js client initialization 10 | options (`authEndpoint` and `authHeaders`). 11 | 12 | POST request is JSON object including two keys: `client` and `channels`. Client is a string with 13 | current client ID and channels is array with one or more channels current user wants to subscribe to. 14 | 15 | I.e sth like this: 16 | 17 | ```javascript 18 | { 19 | "client": "CLIENT ID", 20 | "channels": ["$one"] 21 | } 22 | ``` 23 | 24 | I think it's simplier to explain on example. 25 | 26 | Lets imagine that client wants to subscribe on two private channels: ``$one`` and ``$two``. 27 | 28 | Here is a javascript code to subscribe on them: 29 | 30 | ```javascript 31 | centrifuge.subscribe('$one', function(message) { 32 | // process message 33 | }); 34 | 35 | centrifuge.subscribe('$two', function(message) { 36 | // process message 37 | }); 38 | ``` 39 | 40 | In this case Centrifuge will send two separate POST requests to your web app. There is an 41 | option to batch this requests into one using `startAuthBatching` and `stopAuthBatching` 42 | methods. Like this: 43 | 44 | ```javascript 45 | centrifuge.startAuthBatching(); 46 | 47 | centrifuge.subscribe('$one', function(message) { 48 | // process message 49 | }); 50 | 51 | centrifuge.subscribe('$two', function(message) { 52 | // process message 53 | }); 54 | 55 | centrifuge.stopAuthBatching(); 56 | ``` 57 | 58 | In this case one POST request with 2 channels in `channels` parameter will be sent. 59 | 60 | ```javascript 61 | { 62 | "client": "CLIENT ID", 63 | "channels": ["$one", "$two"] 64 | } 65 | ``` 66 | 67 | What you should return in response - JSON object which contains channels as keys. Every channel key 68 | should have a value containing object with sign parameters. 69 | 70 | If client allowed to subscribe on channel then response JSON will look like this: 71 | 72 | ``` 73 | { 74 | "$one": { 75 | "sign": "PRIVATE SIGN", 76 | "info": "" 77 | } 78 | } 79 | ``` 80 | 81 | * `sign` – private channel subscription sign 82 | * `info` – optional JSON string to be used as `channel_info` (only useful when clients 83 | allowed to publish messages directly). 84 | 85 | See chapter [Tokens and Signatures](../server/tokens_and_signatures.md) to see how to create `sign` string. 86 | 87 | Note, that private channel sign creation already implemented in out API clients. 88 | 89 | If client not allowed to subscribe on channel then return this: 90 | 91 | ``` 92 | { 93 | "$one": { 94 | "status": 403 95 | } 96 | } 97 | ``` 98 | 99 | You can also just return 403 status code for the whole response if client is not allowed to 100 | subscribe on all channels. 101 | 102 | Let's look at simplified example for Tornado how to implement auth endpoint: 103 | 104 | ```python 105 | from cent.core import generate_channel_sign 106 | 107 | class CentrifugeAuthHandler(tornado.web.RequestHandler): 108 | 109 | def check_xsrf_cookie(self): 110 | pass 111 | 112 | def post(self): 113 | 114 | try: 115 | data = json.loads(self.request.body) 116 | except ValueError: 117 | raise tornado.web.HTTPError(403) 118 | 119 | client = data.get("client", "") 120 | channels = data.get("channels", []) 121 | 122 | to_return = {} 123 | 124 | for channel in channels: 125 | info = json.dumps({ 126 | 'channel_extra_info_example': 'you can add additional JSON data when authorizing' 127 | }) 128 | sign = generate_channel_sign( 129 | options.secret_key, client, channel, info=info 130 | ) 131 | to_return[channel] = { 132 | "sign": sign, 133 | "info": info 134 | } 135 | 136 | self.set_header('Content-Type', 'application/json; charset="utf-8"') 137 | self.write(json.dumps(to_return)) 138 | ``` 139 | 140 | In this example we allow user to subscribe on any private channel. If you want to 141 | reject subscription - then you can add "status" key and set it to something not 142 | equal to 200, for example 403: 143 | 144 | ```python 145 | from cent.core import generate_channel_sign 146 | 147 | class CentrifugeAuthHandler(tornado.web.RequestHandler): 148 | 149 | def check_xsrf_cookie(self): 150 | pass 151 | 152 | def post(self): 153 | 154 | try: 155 | data = json.loads(self.request.body) 156 | except ValueError: 157 | raise tornado.web.HTTPError(403) 158 | 159 | client = data.get("client", "") 160 | channels = data.get("channels", []) 161 | 162 | to_return = {} 163 | 164 | for channel in channels: 165 | sign = generate_channel_sign( 166 | options.secret_key, client, channel 167 | ) 168 | to_return[channel] = { 169 | "status": 403, 170 | } 171 | 172 | self.set_header('Content-Type', 'application/json; charset="utf-8"') 173 | self.write(json.dumps(to_return)) 174 | ``` 175 | 176 | If user deactivated in your application then you can just return 403 Forbidden response: 177 | 178 | ```python 179 | from cent.core import generate_channel_sign 180 | 181 | class CentrifugeAuthHandler(tornado.web.RequestHandler): 182 | 183 | def check_xsrf_cookie(self): 184 | pass 185 | 186 | def post(self): 187 | raise tornado.web.HTTPError(403) 188 | ``` 189 | 190 | This will prevent client from subscribing to any private channel. 191 | 192 | If you are developing in PHP (and especially if you use Laravel framework) then 193 | [this gist](https://gist.github.com/Malezha/a9bdfbddee15bfd624d4) can help you working 194 | with private channel subscriptions. 195 | -------------------------------------------------------------------------------- /mixed/websocket_compression.md: -------------------------------------------------------------------------------- 1 | # Websocket compression 2 | 3 | This is an experimental feature for raw websocket endpoint - `permessage-deflate` compression for 4 | websocket messages. Btw look at [great article](https://www.igvita.com/2013/11/27/configuring-and-optimizing-websocket-compression/) about websocket compression. 5 | 6 | We consider this experimental because this websocket compression is experimental in [Gorilla Websocket](https://github.com/gorilla/websocket) library that Centrifugo uses internally. 7 | 8 | Websocket compression can reduce amount of traffic travelling over wire. But keep in mind that 9 | enabling websocket compression will result in much slower Centrifugo performance and more 10 | memory usage - depending on your message rate this can be noticeable. 11 | 12 | To enable websocket compression for raw websocket endpoint set `websocket_compression: true` in 13 | configuration file. After this clients that support permessage-deflate will negotiate compression 14 | with server automatically. Note that enabling compression does not mean that every connection will 15 | use it - this depends on client support for this feature. 16 | 17 | Another option is `websocket_compression_min_size`. Default 0. This is a minimal size of message 18 | in bytes for which we use `deflate` compression when writing it to client's connection. Default 19 | value `0` means that we will compress all messages when `websocket_compression` enabled and 20 | compression support negotiated with client. 21 | 22 | It's also possible to control websocket compression level defined at [compress/flate](https://golang.org/pkg/compress/flate/#NewWriter) 23 | By default when compression with client negotiated Centrifugo uses compression level 1 (BestSpeed). 24 | If you want to set custom compression level use `websocket_compression_level` (new in Centrifugo v1.6.4) 25 | configuration option. 26 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # Server implementation description 2 | 3 | Originally Centrifugal organization started from [Centrifuge](https://github.com/centrifugal/centrifuge) 4 | real-time messaging server written in Python. But then the entire Centrifuge codebase was rewritten 5 | into Go language. So from this moment when we will talk about server we mean [Centrifugo](https://github.com/centrifugal/centrifugo). 6 | 7 | The goal of server is to accept client connections from application (web app, mobile app, 8 | desktop app). Connections can use pure [Websockets](https://developer.mozilla.org/en/docs/WebSockets) 9 | protocol or [SockJS](https://github.com/sockjs/sockjs-client) polyfill library protocol. Centrifugo 10 | keeps accepted connections, deliver different messages from clients to server (for example subscription 11 | command, presence command etc) and from server to clients (new message in channel, various command 12 | responses etc), handle API requests from your web application backend (mostly publish commands to 13 | send new message into channel). 14 | -------------------------------------------------------------------------------- /server/advanced_configuration.md: -------------------------------------------------------------------------------- 1 | # Advanced configuration 2 | 3 | Centrifugo has some options for which default values make sense for most applications. In many case you 4 | don't need (and you really should not) change them. This chapter is about such options. 5 | 6 | #### client_channel_limit 7 | 8 | Default: 128 9 | 10 | Sets maximum number of different channel subscriptions single client can have. 11 | 12 | Before Centrifugo v1.6.0 default value was 100. 13 | 14 | #### max_channel_length 15 | 16 | Default: 255 17 | 18 | Sets maximum length of channel name. 19 | 20 | #### user_connection_limit 21 | 22 | Default: 0 23 | 24 | Maximum number of connections from user (with known ID) to Centrifugo node. By default - unlimited. 25 | 26 | #### node_metrics_interval 27 | 28 | Default: 60 29 | 30 | Interval in seconds Centrifugo aggregates metrics before making metrics snapshot. 31 | 32 | #### client_request_max_size 33 | 34 | Default: 65536 35 | 36 | Maximum allowed size of request from client in bytes. 37 | 38 | #### client_queue_max_size 39 | 40 | Default: 10485760 41 | 42 | Maximum client message queue size in bytes to close slow reader connections. By default - 10mb. 43 | 44 | #### sockjs_heartbeat_delay 45 | 46 | Default: 0 47 | 48 | Interval in seconds how often to send SockJS h-frames to client. Starting from v1.6.0 we don't use hearbeat SockJS 49 | frames as we use client to server pings. 50 | 51 | #### websocket_compression 52 | 53 | Default: false 54 | 55 | Enable websocket compression, see special chapter in docs. 56 | 57 | #### gomaxprocs 58 | 59 | Default: 0 60 | 61 | By default Centrifugo runs on all available CPU cores. If you want to limit amount of cores Centrifugo can utilize in one moment use this option. 62 | 63 | ## Advanced endpoint configuration. 64 | 65 | After you started Centrifugo you have several endpoints available. As soon as you have not provided any extra options you have 3 endpoints by default. 66 | 67 | #### Default endpoints. 68 | 69 | First is SockJS endpoint - it's needed to serve client connections that use SockJS library: 70 | 71 | ``` 72 | http://localhost:8000/connection 73 | ``` 74 | 75 | Next is raw Websocket endpoint to serve client connections that use pure Websocket protocol: 76 | 77 | ``` 78 | ws://localhost:8000/connection/websocket 79 | ``` 80 | 81 | And finally you have API endpoint to `publish` messages to channels (and execute other available API commands): 82 | 83 | ``` 84 | http://localhost:8000/api/ 85 | ``` 86 | 87 | By default all endpoints work on port `8000`. You can change it using `port` option: 88 | 89 | ``` 90 | { 91 | "port": "9000" 92 | } 93 | ``` 94 | 95 | In production setup you will have your domain name in endpoint addresses above instead of `localhost`. Also if your Centrifugo will be behind proxy or load balancer software you most probably won't have ports in your 96 | endpoint addresses. What will always be the same as shown above are URL paths: `/connection`, `/connection/websocket`, `/api/`. 97 | 98 | Let's look at possibilities to tweak available endpoints. 99 | 100 | #### Admin endpoints. 101 | 102 | First is enabling admin endpoints: 103 | 104 | ``` 105 | { 106 | ... 107 | "admin": true, 108 | "admin_password": "password", 109 | "admin_secret": "secret" 110 | } 111 | ``` 112 | 113 | This makes the following endpoint available: 114 | 115 | ``` 116 | ws://localhost:8000/socket 117 | ``` 118 | 119 | This is an endpoint for admin websocket connections. In most scenarios it's used only by our builtin web 120 | interface. You can read about web interface in dedicated chapter. Here we will just show how to enable it: 121 | 122 | ``` 123 | { 124 | ... 125 | "web": true, 126 | "admin": true, 127 | "admin_password": "password", 128 | "admin_secret": "secret" 129 | } 130 | ``` 131 | 132 | After adding `web` option you can visit: 133 | 134 | ``` 135 | http://localhost:8000/ 136 | ``` 137 | 138 | And see web interface. You can log into it using `admin_password` value we set above. 139 | 140 | 141 | #### Debug endpoints. 142 | 143 | Next, when Centrifugo started in debug mode some extra debug endpoints become available. 144 | To start in debug mode add `debug` option to config: 145 | 146 | ``` 147 | { 148 | ... 149 | "debug": true 150 | } 151 | ``` 152 | 153 | And endpoint: 154 | 155 | ``` 156 | http://localhost:8000/debug/pprof/ 157 | ``` 158 | 159 | will show you useful info about internal state of Centrifugo instance. This info is especially helpful when troubleshooting. 160 | 161 | #### Custom admin and API ports 162 | 163 | We strongly recommend to not expose admin (web), debug and API endpoints to internet. In case of admin endpoints this step 164 | provides extra protection to `/socket` endpoint, web interface and debug endpoints. Protecting API endpoint will allow you to use `insecure_api` 165 | mode to omit signing of each API request. 166 | 167 | So it's a good practice to protect admin and API endpoints with firewall. For example you can do this in `location` section of Nginx configuration. 168 | 169 | Though sometimes you don't have access to per-location configuration in your proxy/load balancer software. For example 170 | when using Amazon ELB. In this case you can change ports on which your admin and API endpoints work. 171 | 172 | To run admin endpoints on custom port use `admin_port` option: 173 | 174 | ``` 175 | { 176 | ... 177 | "admin_port": "10000" 178 | } 179 | ``` 180 | 181 | So admin socket will work on address: 182 | 183 | ``` 184 | ws://localhost:10000/socket 185 | ``` 186 | 187 | And debug page will be available on new custom admin port too: 188 | 189 | ``` 190 | http://localhost:10000/debug/pprof/ 191 | ``` 192 | 193 | To run API server on it's own port use `api_port` option: 194 | 195 | ``` 196 | { 197 | ... 198 | "api_port": "10001" 199 | } 200 | ``` 201 | 202 | Now you should send API requests to: 203 | 204 | ``` 205 | http://localhost:10001/api/ 206 | ``` 207 | -------------------------------------------------------------------------------- /server/api.md: -------------------------------------------------------------------------------- 1 | # Server API 2 | 3 | HTTP API is a way to send commands to Centrifugo. There is also another way to send commands – 4 | using Redis engine but in this chapter we will talk mostly about HTTP API. 5 | 6 | Why we need API? 7 | 8 | If you look at project and namespace options you see option called `publish`. When turned on 9 | this option allows browser clients to publish into channels directly. If client publishes a 10 | message into channel directly - your application will not receive that message (it just goes 11 | through Centrifugo towards subscribed clients). This pattern can be useful sometimes but in 12 | most cases you first need to receive new event from client via AJAX, process it - probably 13 | validate, save into database and then `publish` into Centrifugo using HTTP API and Centrifugo 14 | will then broadcast message to all subscribed clients. 15 | 16 | Also HTTP API can be used to send other types of commands - see all available commands below. 17 | 18 | If your backend written in Python you can use [Cent](../libraries/python.md) API client. Also we have 19 | client [for Ruby](../libraries/ruby.md) and [PHP](../libraries/php.md). If you use other language don't worry - I will describe 20 | how to communicate with Centrifugo HTTP API endpoint in this chapter. 21 | 22 | Note also that there are 2 API endpoints in Centrifugo - first of them is HTTP API endpoint and 23 | second – Redis Engine API. In this section we will talk about HTTP API mostly. You can find 24 | description of Redis API in `engines` chapter - it has the same commands but simplified format. 25 | So if you decide that HTTP API is too difficult and uncomfortable to use - you can use Redis API 26 | to publish new messages into channels. 27 | 28 | Let's start! 29 | 30 | Centrifugo API url is `/api/`. 31 | 32 | So if your Centrifugo sits on domain `https://centrifuge.example.com` then an API address 33 | will be `https://centrifuge.example.com/api/`. 34 | 35 | All you need to do to use HTTP API is to send correctly constructed POST request to this endpoint. 36 | 37 | API request is a POST `application/json` request with commands in request body and one additional header `X-API-Sign`. 38 | 39 | Body of request is just a JSON representing commands you need to execute. This can be single command 40 | or array of commands. See available commands below. 41 | 42 | `X-API-Sign` header is an SHA-256 HMAC string based on Centrifugo secret key and JSON body you want to send. 43 | This header is used by Centrifugo to validate API request. In most situations you can protect Centrifugo 44 | API endpoint with firewall rules and disable sign check using `--insecure_api` option when starting 45 | Centrifugo. In this case you just need to send POST request with commands - no need in addition header. 46 | 47 | Command is a JSON object with two properties: `method` and `params`. 48 | 49 | `method` is a name of action you want to do. 50 | `params` is an object with method arguments. 51 | 52 | For example to send publish command to Centrifugo in Python you construct sth like this for your request body: 53 | 54 | ``` 55 | command = json.dumps({ 56 | "method": "publish", 57 | "params": {"channel": "news", data:{}} 58 | }) 59 | ``` 60 | 61 | If you have not turned of sign check you also need to include properly constructed sign 62 | in `X-API-Sign` header when sending this to `/api/` Centrifugo endpoint. 63 | 64 | To send 2 publish commands in one request you need body like this: 65 | 66 | ``` 67 | commands = json.dumps([ 68 | { 69 | "method": "publish", 70 | "params": {"channel": "news", data:{"content": "1"}} 71 | }, 72 | { 73 | "method": "publish", 74 | "params": {"channel": "news", data:{"content": "2"}} 75 | } 76 | ]) 77 | ``` 78 | 79 | First lets see how to construct such request in Python. 80 | 81 | *If Python is your language then you don't have to implement this yourself as 82 | `Cent` python module exists.* 83 | 84 | But this example here can be useful for someone who want to implement interaction 85 | with Centrifugo API in language for which we don't have API client yet or you just 86 | want to send requests yourself - this is simple and in most cases you can just go 87 | without using our API library (to not introduce extra dependency in your project for 88 | example). 89 | 90 | Let's imagine that your Centrifugo has secret key `secret`. 91 | 92 | First, let's see how to send API command using Python library `requests`: 93 | 94 | ```python 95 | import json 96 | import requests 97 | 98 | from cent.core import generate_api_sign 99 | 100 | 101 | commands = [ 102 | { 103 | "method": "publish", 104 | "params": {"channel": "docs", "data": {"content": "1"}} 105 | } 106 | ] 107 | encoded_data = json.dumps(commands) 108 | sign = generate_api_sign("secret", encoded_data) 109 | headers = {'Content-type': 'application/json', 'X-API-Sign': sign} 110 | r = requests.post("https://centrifuge.example.com/api/", data=encoded_data, headers=headers) 111 | print r.json() 112 | ``` 113 | 114 | In code above to generate sign we used function from `Cent` module. To see how you can 115 | generate API sign yourself go to chapter [Tokens and signatures](./tokens_and_signatures.md). 116 | 117 | Also note that in this example we send an array of commands. In this way you can send several 118 | commands to Centrifugo in one request. 119 | 120 | There are not so many commands you can call. The main and most useful of them is `publish`. 121 | Lets take a closer look on other available API command methods. 122 | 123 | You have `publish`, `broadcast`, `unsubscribe`, `presence`, `history`, `disconnect`, 124 | `channels`, `stats`, `node` in your arsenal. 125 | 126 | ### publish 127 | 128 | `publish` allows to send message into channel. `params` for `publish` method must be an 129 | object with two keys: `channel` and `data` which contains valid JSON payload you want to 130 | send into channel. 131 | 132 | ```javascript 133 | { 134 | "method": "publish", 135 | "params": { 136 | "channel": "CHANNEL NAME", 137 | "data": { 138 | "input": "hello" 139 | } 140 | } 141 | } 142 | ``` 143 | 144 | Starting with **version 0.2.0** there is an option to include `client` ID into publish API command: 145 | 146 | ```javascript 147 | { 148 | "method": "publish", 149 | "params": { 150 | "channel": "CHANNEL NAME", 151 | "data": { 152 | "input": "hello" 153 | }, 154 | "client": "long-unique-client-id" 155 | } 156 | } 157 | ``` 158 | 159 | In most cases this is a `client` ID that initiated this message. This `client` will 160 | be then added on top level of published message. 161 | 162 | Response example: 163 | 164 | ```javascript 165 | { 166 | "body": null, 167 | "error": null, 168 | "method": "publish" 169 | } 170 | ``` 171 | 172 | ### broadcast (new in v1.2.0) 173 | 174 | Very similar to `publish` but allows to send the same data into many channels. 175 | 176 | ```javascript 177 | { 178 | "method": "broadcast", 179 | "params": { 180 | "channels": ["CHANNEL_1", "CHANNEL_2"], 181 | "data": { 182 | "input": "hello" 183 | } 184 | } 185 | } 186 | ``` 187 | 188 | `client` field to set client ID also supported. 189 | 190 | This command will publish data into channels until first error happens. This error then set 191 | as response error and publishing stops. In case of using Redis API queue first error will 192 | be logged. 193 | 194 | 195 | ### unsubscribe 196 | 197 | `unsubscribe` allows to unsubscribe user from channel. `params` is an objects with two 198 | keys: `channel` and `user` (user ID you want to unsubscribe) 199 | 200 | ```javascript 201 | { 202 | "method": "unsubscribe", 203 | "params": { 204 | "channel": "CHANNEL NAME", 205 | "user": "USER ID" 206 | } 207 | } 208 | ``` 209 | 210 | Response example: 211 | 212 | ```javascript 213 | { 214 | "body": null, 215 | "error": null, 216 | "method": "unsubscribe" 217 | } 218 | ``` 219 | 220 | 221 | ### disconnect 222 | 223 | `disconnect` allows to disconnect user by its ID. `params` in an object with `user` key. 224 | 225 | ```javascript 226 | { 227 | "method": "disconnect", 228 | "params": { 229 | "user": "USER ID" 230 | } 231 | } 232 | ``` 233 | 234 | Response example: 235 | 236 | ```javascript 237 | { 238 | "body": null, 239 | "error": null, 240 | "method": "disconnect" 241 | } 242 | ``` 243 | 244 | 245 | ### presence 246 | 247 | `presence` allows to get channel presence information (all clients currently subscribed on 248 | this channel). `params` is an object with `channel` key. 249 | 250 | ```javascript 251 | { 252 | "method": "presence", 253 | "params": { 254 | "channel": "CHANNEL NAME" 255 | } 256 | } 257 | ``` 258 | 259 | Response example: 260 | 261 | ```javascript 262 | { 263 | "body": { 264 | "channel": "$public:chat", 265 | "data": { 266 | "a1c2f99d-fdaf-4e00-5f73-fc8a6bb7d239": { 267 | "user": "2694", 268 | "client": "a1c2f99d-fdaf-4e00-5f73-fc8a6bb7d239", 269 | "default_info": { 270 | "first_name": "Alexandr", 271 | "last_name": "Emelin" 272 | }, 273 | "channel_info": { 274 | "channel_extra_info_example": "you can add additional JSON data when authorizing" 275 | } 276 | }, 277 | "e5ee0ab0-fde1-4543-6f36-13f2201adeac": { 278 | "user": "2694", 279 | "client": "e5ee0ab0-fde1-4543-6f36-13f2201adeac", 280 | "default_info": { 281 | "first_name": "Alexandr", 282 | "last_name": "Emelin" 283 | }, 284 | "channel_info": { 285 | "channel_extra_info_example": "you can add additional JSON data when authorizing" 286 | } 287 | } 288 | } 289 | }, 290 | "error": null, 291 | "method": "presence" 292 | } 293 | ``` 294 | 295 | 296 | ### history 297 | 298 | `history` allows to get channel history information (list of last messages sent into channel). 299 | `params` is an object with `channel` key. 300 | 301 | ```javascript 302 | { 303 | "method": "history", 304 | "params": { 305 | "channel": "CHANNEL NAME" 306 | } 307 | } 308 | ``` 309 | 310 | Response example: 311 | 312 | ```javascript 313 | { 314 | "body": { 315 | "channel": "$public:chat", 316 | "data": [ 317 | { 318 | "uid": "8c5dca2e-1846-42e4-449e-682f615c4977", 319 | "timestamp": "1445536974", 320 | "info": { 321 | "user": "2694", 322 | "client": "a1c2f99d-fdaf-4e00-5f73-fc8a6bb7d239", 323 | "default_info": { 324 | "first_name": "Alexandr", 325 | "last_name": "Emelin" 326 | }, 327 | "channel_info": { 328 | "channel_extra_info_example": "you can add additional JSON data when authorizing" 329 | } 330 | }, 331 | "channel": "$public:chat", 332 | "data": { 333 | "input": "world" 334 | }, 335 | "client": "a1c2f99d-fdaf-4e00-5f73-fc8a6bb7d239" 336 | }, 337 | { 338 | "uid": "63ecba35-e9df-4dc6-4b72-a22f9c9f486f", 339 | "timestamp": "1445536969", 340 | "info": { 341 | "user": "2694", 342 | "client": "a1c2f99d-fdaf-4e00-5f73-fc8a6bb7d239", 343 | "default_info": { 344 | "first_name": "Alexandr", 345 | "last_name": "Emelin" 346 | }, 347 | "channel_info": { 348 | "channel_extra_info_example": "you can add additional JSON data when authorizing" 349 | } 350 | }, 351 | "channel": "$public:chat", 352 | "data": { 353 | "input": "hello" 354 | }, 355 | "client": "a1c2f99d-fdaf-4e00-5f73-fc8a6bb7d239" 356 | } 357 | ] 358 | }, 359 | "error": null, 360 | "method": "history" 361 | } 362 | ``` 363 | 364 | 365 | ### channels (Centrifugo >= 0.3.0) 366 | 367 | `channels` method allows to get list of active (with one or more subscribers) channels. 368 | 369 | ```javascript 370 | { 371 | "method": "channels", 372 | "params": {} 373 | } 374 | ``` 375 | 376 | Response example: 377 | 378 | ```javascript 379 | { 380 | "body": { 381 | "data": [ 382 | "$public:chat", 383 | "news", 384 | "notifications" 385 | ] 386 | }, 387 | "error": null, 388 | "method": "channels" 389 | } 390 | ``` 391 | 392 | ### stats (Centrifugo >= 1.0.0) 393 | 394 | `stats` method allows to get statistics about running Centrifugo nodes. 395 | 396 | ```javascript 397 | { 398 | "method": "stats", 399 | "params": {} 400 | } 401 | ``` 402 | 403 | Response example: 404 | 405 | ```javascript 406 | { 407 | "body": { 408 | "data": { 409 | "nodes": [ 410 | { 411 | "uid": "6045438c-1b65-4b86-79ee-0c35367f29a9", 412 | "name": "MacAir.local_8000", 413 | "num_goroutine": 21, 414 | "num_clients": 0, 415 | "num_unique_clients": 0, 416 | "num_channels": 0, 417 | "started_at": 1445536564, 418 | "gomaxprocs": 1, 419 | "num_cpu": 4, 420 | "num_msg_published": 0, 421 | "num_msg_queued": 0, 422 | "num_msg_sent": 0, 423 | "num_api_requests": 0, 424 | "num_client_requests": 0, 425 | "bytes_client_in": 0, 426 | "bytes_client_out": 0, 427 | "memory_sys": 7444728, 428 | "cpu_usage": 0 429 | } 430 | ], 431 | "metrics_interval": 60 432 | } 433 | }, 434 | "error": null, 435 | "method": "stats" 436 | } 437 | ``` 438 | 439 | ### node (Centrifugo >= 1.4.0) 440 | 441 | `node` method allows to get information about single Centrifugo node. That information 442 | will contain counters without aggregation over minute interval (what `stats` method 443 | does by default). So it can be useful if your metric aggregation system aggregates counters 444 | over time period itself. Also note that to use this method you should send API request 445 | to each Centrifugo node separately - as this method returns current raw statistics about 446 | node. See [issue](https://github.com/centrifugal/centrifugo/issues/68) for motivation 447 | description. 448 | 449 | ```javascript 450 | { 451 | "method": "node", 452 | "params": {} 453 | } 454 | ``` 455 | 456 | Response example: 457 | 458 | ``` 459 | { 460 | "body": { 461 | "data":{ 462 | "uid":"c3ceab87-8060-4c25-9cb4-94eb9db7899a", 463 | "name":"MacAir.local_8000", 464 | "num_goroutine":14, 465 | "num_clients":0, 466 | "num_unique_clients":0, 467 | "num_channels":0, 468 | "started_at":1455450238, 469 | "gomaxprocs":4, 470 | "num_cpu":4, 471 | "num_msg_published":0, 472 | "num_msg_queued":0, 473 | "num_msg_sent":0, 474 | "num_api_requests":3, 475 | "num_client_requests":0, 476 | "bytes_client_in":0, 477 | "bytes_client_out":0, 478 | "memory_sys":0, 479 | "cpu_usage":0 480 | } 481 | }, 482 | "error":null, 483 | "method":"node" 484 | } 485 | ``` 486 | 487 | 488 | Note again that there is existing API clients for Python, Ruby, PHP - so you don't 489 | have to construct these commands manually. If you use another programming languages 490 | look at existing clients to get more help implementing call to HTTP API. 491 | -------------------------------------------------------------------------------- /server/channels.md: -------------------------------------------------------------------------------- 1 | # Channels 2 | 3 | Channel is a route for messages. 4 | 5 | Clients can subscribe on channel to receive events related to this channel - new 6 | messages, join/leave events etc. Also client must be subscribed on channel to get 7 | presence or history information. 8 | 9 | Channel is just a string - ``news``, ``comments`` are valid channel names. 10 | 11 | **BUT!** You should remember several things. 12 | 13 | First, channel name length is limited by `255` characters by default (can 14 | be changed via configuration file option `max_channel_length`) 15 | 16 | Second, `:`, `#`, `&` and `$` symbols have a special role in channel name. 17 | 18 | ### namespace channel boundary 19 | 20 | ``:`` - is a channel namespace boundary. 21 | 22 | If channel is `public:chat` - then Centrifuge will apply options to this channel 23 | from channel namespace with name `public`. 24 | 25 | ### user channel boundary 26 | 27 | `#` is a user boundary - separator to create private channels for users (user limited 28 | channels) without sending POST request to your web application. For example if channel 29 | is `news#42` then only user with ID `42` can subscribe on this channel (Centrifugo 30 | knows user ID as clients provide it in connection parameters). 31 | 32 | Moreover you can provide several user IDs in channel name separated by comma: `dialog#42,43` – 33 | in this case only user with ID `42` and user with ID `43` will be able to subscribe on this channel. 34 | 35 | This is useful for channels with static allowed users, for example for user personal messages 36 | channel, for dialog channel between certainly defined users. As soon as you need dynamic user 37 | access to channel this channel type does not suit well. 38 | 39 | ### client channel boundary (new in 0.2.0) 40 | 41 | `&` is a client channel boundary. This is similar to user channel boundary but limits 42 | access to channel only for one client with ID set after `&`. For example if channel is 43 | `client&7a37e561-c720-4608-52a8-a964a9db7a8a` then only client with client ID 44 | `7a37e561-c720-4608-52a8-a964a9db7a8a` (call `centrifuge.getClientId()` in javascript to 45 | get client's ID) will be able to subscribe on this channel. 46 | 47 | ### private channel prefix 48 | 49 | If channel starts with `$` then it considered private. Subscription on private channel 50 | must be properly signed by your web application. Read special chapter in docs about 51 | private channel subscriptions. 52 | -------------------------------------------------------------------------------- /server/client_protocol.md: -------------------------------------------------------------------------------- 1 | # Client protocol description 2 | 3 | This chapter aims to help developers to implement new client library or understand 4 | how already implemented clients work. This chapter is not complete. I will 5 | update it from time to time with additional information. 6 | 7 | Centrifugo already has Javascript, Go, iOS, Android, Python clients to connect your application 8 | users. 9 | 10 | One of the ways to understand how to implement new client is looking at source code 11 | of existing clients, for example [centrifuge-js](https://github.com/centrifugal/centrifuge-js/blob/master/src/centrifuge.js) or [centrifuge-python](https://github.com/centrifugal/centrifuge-python). 12 | 13 | Currently websocket is the only available transport to implement client. Centrifugo also supports 14 | SockJS connections from browser but it's only for browser usage, there is no reason to use it 15 | from other environments. All communication done via exchanging JSON messages. 16 | 17 | Let's look at client protocol step-by-step. 18 | 19 | ### Connect, subscribe on channel and wait for published messages 20 | 21 | Websocket endpoint is: 22 | 23 | ``` 24 | ws://your_centrifugo_server.com/connection/websocket 25 | ``` 26 | 27 | Or in case of using TLS: 28 | 29 | ``` 30 | wss://your_centrifugo_server.com/connection/websocket 31 | ``` 32 | 33 | What client should do first is to create Websocket connection to this endpoint. 34 | 35 | After successful connection client must send `connect` command to server to authorize itself. 36 | 37 | `connect` command is a JSON structure like this (all our examples here use Javascript 38 | language, but the same can be applied to any language): 39 | 40 | ```javascript 41 | var message = { 42 | "uid": "UNIQUE COMMAND ID", 43 | "method": "connect", 44 | "params": { 45 | "user": "USER ID STRING", 46 | "timestamp": "STRING WITH CURRENT TIMESTAMP SECONDS", 47 | "info": "OPTIONAL JSON ENCODED STRING", 48 | "token": "SHA-256 HMAC TOKEN GENERATED FROM PARAMETERS ABOVE" 49 | } 50 | } 51 | 52 | connection.send(JSON.stringify(message)) 53 | ``` 54 | 55 | Look at `method` key with a name of our command – `connect`. 56 | 57 | Centrifugo can parse an array of messages in one request, so you can add command above into 58 | array and send result to Centrifugo server over Websocket connection established before: 59 | 60 | ```javascript 61 | var messages = [message] 62 | connection.send(JSON.stringify(messages)) 63 | ``` 64 | 65 | Description of `connect` command parameters described in a chapter about javascript client. 66 | 67 | In short here: 68 | 69 | * `user` - current application user ID (string) 70 | * `timestamp` - current Unix timestamp as seconds (string) 71 | * `info` - optional JSON string with client additional information (string) 72 | * `token` - SHA-256 HMAC token generated on backend (based on secret key from 73 | Centrifugo configuration) to sign parameters above. 74 | 75 | Application backend must provide all these connection parameters (together with generated 76 | HMAC SHA-256 token) to client (pass to template when client opens web page for example). 77 | 78 | After receiving `connect` command over Websocket connection Centrifugo server uses the 79 | same algorithm (HMAC SHA-256) to generate the token. Correct token proves that client 80 | provided valid user ID, timestamp and info in its `connect` message. 81 | 82 | *Note* that Centrifugo can also allow non-authenticated users to connect to it (for example 83 | sites with public stream with notifications where all visitors can see new events in real-time 84 | without actually logging in). For this case backend must generate token using empty string as 85 | user ID. In this scenario `anonymous` access must be enabled for channels explicitly in 86 | configuration of Centrifugo. 87 | 88 | What you should do next is wait for response from server to `connect` command you just sent. 89 | 90 | In general structure that will come from Centrifugo server to your client looks like this: 91 | 92 | ```javascript 93 | [{response}, {response}, {response}] 94 | ``` 95 | 96 | Or just single response 97 | 98 | ``` 99 | {response} 100 | ``` 101 | 102 | I.e. array of responses or one response to commands you sent before. I.e. in our case Centrifugo 103 | will send to our client: 104 | 105 | ```javascript 106 | [{connect_command_response}] 107 | ``` 108 | 109 | Or just: 110 | 111 | ```javascript 112 | {connect_command_response} 113 | ``` 114 | 115 | So **client must be ready to process both arrays of responses and single object response. This rule 116 | applies to all communication**. 117 | 118 | Every `response` is a structure like this: 119 | 120 | ```javascript 121 | { 122 | "uid": "ECHO BACK THE SAME UNIQUE COMMAND ID SENT IN REQUEST COMMAND", 123 | "method": "COMMAND NAME TO WHICH THIS RESPONSE REFERS TO", 124 | "error": "ERROR STRING, IF NOT EMPTY THEN SOMETHING WENT WRONG AND BODY SHOULD NOT BE PROCESSED", 125 | "body": "RESPONSE BODY, CONTAINS USEFUL RESPONSE DATA" 126 | } 127 | ``` 128 | 129 | Javascript client uses `method` key to understand what to do with response. As Javascript is 130 | evented IO language it just calls corresponding function to react on response. Unique `uid` 131 | also can be used to implement proper responses handling in other languages. For example Go 132 | client remember command `uid` to call some callback when it receives response from Centrifugo. 133 | 134 | General rule - if response contains a non-empty `error` then server returned an error. 135 | 136 | You should not get errors in normal workflow. If you get an error then most probably you 137 | are doing something wrong and this must be fixed on development stages. It can also be 138 | `internal server error` from Centrifugo. Only developers should see text of protocol errors 139 | – they are not supposed to be shown to your application clients. 140 | 141 | In case of successful `connect` response body is: 142 | 143 | ```javascript 144 | { 145 | "client": "UNIQUE CLIENT ID SERVER GAVE TO THIS CONNECTION", 146 | "expires": "false", 147 | "expired": false, 148 | "ttl": 0 149 | } 150 | ``` 151 | 152 | At moment let's just speak about `client` key. This is unique client ID Centrifugo set 153 | to this connection. 154 | 155 | As soon your client successfully connected and got its unique connection ID it is ready to 156 | subscribe on channels. 157 | 158 | ```javascript 159 | var message = { 160 | 'uid': 'UNIQUE COMMAND ID', 161 | 'method': 'subscribe', 162 | 'params': { 163 | 'channel': "CHANNEL TO SUBSCRIBE" 164 | } 165 | } 166 | ``` 167 | 168 | Just send this `subscribe` command in the same way as `connect` command before. 169 | 170 | After you received successful response on this `subscribe` command your client will receive 171 | messages published to this channel. Those messages will be delivered through Websocket 172 | connection as response with method `message`. I.e. response will look like this: 173 | 174 | ``` 175 | { 176 | "method": "message", 177 | "body": { 178 | "uid": "8d1f6279-2d13-45e2-542d-fac0e0f1f6e0", 179 | "info":{ 180 | "user":"42", 181 | "client":"73cd5abb-03ed-40bc-5c87-ed35df732682", 182 | "default_info":null, 183 | "channel_info":null 184 | }, 185 | "channel":"jsfiddle-chat", 186 | "data": { 187 | "input":"hello world" 188 | }, 189 | "client":"73cd5abb-03ed-40bc-5c87-ed35df732682" 190 | } 191 | } 192 | ``` 193 | 194 | `body` of `message` response contains `channel` to which message corresponds and `data` 195 | key - this is an actual JSON that was published into that channel. 196 | 197 | This is enough to start with - client established connection, authorized itself sending `connect` 198 | command, subscribed on channel to receive new messages published into that channel. This is a core 199 | Centrifugo functionality. There are lots of other things to cover – channel presence information, 200 | channel history information, connection expiration, private channel subscriptions, join/leave events 201 | and more but in most cases all you need from Centrifugo - subscribe on channels and receive new 202 | messages from those channels as soon as your backend published them into Centrifugo server API. 203 | 204 | ### Available methods 205 | 206 | Lets now look at all available methods your client can send or receive: 207 | 208 | ``` 209 | connect 210 | disconnect 211 | subscribe 212 | unsubscribe 213 | publish 214 | presence 215 | history 216 | join 217 | leave 218 | message 219 | refresh 220 | ping 221 | ``` 222 | 223 | Some of this methods used for client to server commands (`publish`, `presence`, `history` etc which 224 | then get a response from server with the same `method` and unique `uid` in it), some for server to 225 | clients (for example `join`, `leave`, `message` – which just come from server in any time when 226 | corresponding event occurred). 227 | 228 | We have already seen `connect`, `subscribe` and `publish` above. Let's describe remaining. 229 | 230 | ### Client to server commands 231 | 232 | `connect` - send authorization parameters to Centrifugo so your connection could start subscribing 233 | on channels. 234 | 235 | ```javascript 236 | var message = { 237 | 'uid': 'UNIQUE COMMAND ID', 238 | 'method': 'connect', 239 | 'params': { 240 | 'user': "USER ID STRING", 241 | 'timestamp': "STRING WITH CURRENT TIMESTAMP SECONDS" 242 | 'info': "OPTIONAL JSON ENCODED STRING", 243 | 'token': "SHA-256 HMAC TOKEN GENERATED FROM PARAMETERS ABOVE" 244 | } 245 | } 246 | ``` 247 | 248 | `subscribe` - allows to subscribe on channel after client successfully connected 249 | 250 | ```javascript 251 | var message = { 252 | 'uid': 'UNIQUE COMMAND ID', 253 | 'method': 'subscribe', 254 | 'params': { 255 | 'channel': "CHANNEL TO SUBSCRIBE" 256 | } 257 | } 258 | ``` 259 | 260 | `unsubscribe` - allows to unsubscribe from channel 261 | 262 | ```javascript 263 | message = { 264 | 'uid': 'UNIQUE COMMAND ID', 265 | "method": "unsubscribe", 266 | "params": { 267 | "channel": "CHANNEL TO UNSUBSCRIBE" 268 | } 269 | } 270 | ``` 271 | 272 | `publish` - allows clients directly publish messages into channel (application backend code will never 273 | know about this message). `publish` must be enabled for channel in sever configuration so this command 274 | can work (otherwise Centrifugo will return `permission denied` error in response). 275 | 276 | ```javascript 277 | message = { 278 | 'uid': 'UNIQUE COMMAND ID', 279 | "method": "publish", 280 | "params": { 281 | "channel": "CHANNEL", 282 | "data": {} // JSON DATA TO PUBLISH 283 | } 284 | } 285 | ``` 286 | 287 | `presence` – allows to ask server for channel presence information (`presence` must be enabled for 288 | channel in server configuration or Centrifugo will return `not available` error in response) 289 | 290 | ```javascript 291 | message = { 292 | 'uid': 'UNIQUE COMMAND ID', 293 | "method": "presence", 294 | "params": { 295 | "channel": "CHANNEL" 296 | } 297 | } 298 | ``` 299 | 300 | `history` – allows to ask server for channel history information (history must be enabled for 301 | channel in server configuration using `history_lifetime` and `history_size` options or Centrifugo 302 | will return `not available` error in response) 303 | 304 | ```javascript 305 | message = { 306 | 'uid': 'UNIQUE COMMAND ID', 307 | "method": "history", 308 | "params": { 309 | "channel": "CHANNEL" 310 | } 311 | } 312 | ``` 313 | 314 | `ping` - allows to send ping command to server, server will answer this command with `ping` 315 | response. 316 | 317 | ```javascript 318 | message = { 319 | 'uid': 'UNIQUE COMMAND ID', 320 | "method": "ping" 321 | } 322 | ``` 323 | 324 | ### Responses of client to server commands 325 | 326 | As soon as your client sent command to server it should then receive a corresponding response. 327 | Let's look at those response messages in detail. 328 | 329 | TODO: write about responses 330 | 331 | ### Server to client commands 332 | 333 | `message` - new message published into channel current client subscribed to. Response 334 | for message coming over connection looks like this: 335 | 336 | ```javascript 337 | { 338 | "method":"message", 339 | "body": { 340 | "uid": "8d1f6279-2d13-45e2-542d-fac0e0f1f6e0", 341 | "info":{ 342 | "user":"42", 343 | "client":"73cd5abb-03ed-40bc-5c87-ed35df732682", 344 | "default_info":null, 345 | "channel_info":null 346 | }, 347 | "channel":"jsfiddle-chat", 348 | "data": { 349 | "input":"hello world" 350 | }, 351 | "client":"73cd5abb-03ed-40bc-5c87-ed35df732682" 352 | } 353 | } 354 | ``` 355 | 356 | `join` - someone joined a channel current client subscribed to. Note that `join_leave` option must 357 | be enabled for channel in server configuration to receive this type of messages. `body` of this message 358 | contains information about new subscribed client. 359 | 360 | ```javascript 361 | { 362 | "method":"join", 363 | "body": { 364 | "channel":"$public:chat", 365 | "data": { 366 | "user":"2694", 367 | "client":"3702659c-f28a-4166-5b44-115d9b544b29", 368 | "default_info": { 369 | "first_name":"Alexandr", 370 | "last_name":"Emelin" 371 | }, 372 | "channel_info": { 373 | "channel_extra_info_example":"you can add additional JSON data when authorizing" 374 | } 375 | } 376 | } 377 | } 378 | ``` 379 | 380 | `leave` - someone left channel current client subscribed to. Note that `join_leave` option must 381 | be enabled for channel in server configuration to receive this type of messages. `body` of this message 382 | contains information about unsubscribed client. 383 | 384 | ```javascript 385 | { 386 | "method":"leave", 387 | "body": { 388 | "channel":"$public:chat", 389 | "data": { 390 | "user":"2694", 391 | "client":"3702659c-f28a-4166-5b44-115d9b544b29", 392 | "default_info": { 393 | "first_name":"Alexandr", 394 | "last_name":"Emelin" 395 | }, 396 | "channel_info": { 397 | "channel_extra_info_example":"you can add additional JSON data when authorizing" 398 | } 399 | } 400 | } 401 | } 402 | ``` 403 | 404 | ### Private channel subscriptions. 405 | 406 | As you could see successful connect response body has `client` field - a unique connection ID issued 407 | by Centrifugo to this particular client connection. It's important because it's used when obtaining 408 | private channel sign. 409 | 410 | We've already seen above that in general case (non-private channel subscription) subscription request 411 | that must be sent by client to Centrifugo looks like this: 412 | 413 | ```javascript 414 | var message = { 415 | 'uid': 'UNIQUE COMMAND ID', 416 | 'method': 'subscribe', 417 | 'params': { 418 | 'channel': "CHANNEL TO SUBSCRIBE" 419 | } 420 | } 421 | ``` 422 | 423 | When subscribing on private channel client must also provide additional fields in `params` object: 424 | 425 | ```javascript 426 | var message = { 427 | 'uid': 'UNIQUE COMMAND ID', 428 | 'method': 'subscribe', 429 | 'params': { 430 | 'channel': "channel to subscribe", 431 | 'client': "current client ID", 432 | 'info': "additional private channel JSON string info", 433 | 'sign': "string channel sign generated on app backend based on client ID and optional info" 434 | } 435 | } 436 | ``` 437 | 438 | See [chapter about signs](./tokens_and_signatures.md) to get more knowledge about how to generate such 439 | private channel sign on your backend side. 440 | 441 | In case of Javascript client we send client ID with private channel names to backend automatically 442 | in AJAX request so all that developer needs is to check user permissions (as we call backend via 443 | AJAX from browser user will be properly set by application backend session mechanism), generate 444 | valid private channel sign and return in response. In case of other clients (for example mobile) 445 | there is no convenient way (such as AJAX in web) to get data from backend - so it's up to developer 446 | to decide how he wants to obtain channel sign. 447 | 448 | Client library should at least provide mechanism to give developer client ID of current connection 449 | and mechanism to set `client`, `info` and `sign` fields to subscription request `params`. As client ID 450 | will change after reconnect every time client wants to subscribe on private channel backend must 451 | generate new channel sign. So every time client library wants to send private subscription request it 452 | must first ask application code for new private channel sign. 453 | 454 | To be continued... 455 | 456 | -------------------------------------------------------------------------------- /server/configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration overview 2 | 3 | Centrifugo expects JSON, TOML or YAML formats as format of configuration file. 4 | Thanks to brilliant Go library for application configuration - [viper](https://github.com/spf13/viper). 5 | 6 | But first let's inspect all available command-line options: 7 | 8 | ```bash 9 | centrifugo -h 10 | ``` 11 | 12 | You should see something like this as output: 13 | 14 | ``` 15 | Centrifugo. Real-time messaging (Websockets or SockJS) server in Go. 16 | 17 | Usage: 18 | [flags] 19 | [command] 20 | Available Commands: 21 | version Centrifugo version number 22 | checkconfig Check configuration file 23 | genconfig Generate simple configuration file to start with 24 | help Help about any command 25 | 26 | Flags: 27 | -a, --address string address to listen on 28 | --admin enable admin socket 29 | --admin_port string port to bind admin endpoints to (optional) 30 | --api_port string port to bind api endpoints to (optional) 31 | -c, --config string path to config file (default "config.json") 32 | -d, --debug enable debug mode 33 | -e, --engine string engine to use: memory or redis (default "memory") 34 | --insecure start in insecure client mode 35 | --insecure_admin use insecure admin mode – no auth required for admin socket 36 | --insecure_api use insecure API mode 37 | --log_file string optional log file - if not specified logs go to STDOUT 38 | --log_level string set the log level: trace, debug, info, error, critical, fatal or none (default "info") 39 | -n, --name string unique node name 40 | --pid_file string optional path to create PID file 41 | -p, --port string port to bind HTTP server to (default "8000") 42 | --redis_api enable Redis API listener (Redis engine) 43 | --redis_api_num_shards int Number of shards for redis API queue (Redis engine) 44 | --redis_db string redis database (Redis engine) (default "0") 45 | --redis_host string redis host (Redis engine) (default "127.0.0.1") 46 | --redis_master_name string Name of Redis master Sentinel monitors (Redis engine) 47 | --redis_password string redis auth password (Redis engine) 48 | --redis_pool int Redis pool size (Redis engine) (default 256) 49 | --redis_port string redis port (Redis engine) (default "6379") 50 | --redis_sentinels string Comma separated list of Sentinels (Redis engine) 51 | --redis_url string redis connection URL in format redis://:password@hostname:port/db (Redis engine) 52 | --ssl accept SSL connections. This requires an X509 certificate and a key file 53 | --ssl_cert string path to an X509 certificate file 54 | --ssl_key string path to an X509 certificate key 55 | -w, --web serve admin web interface application (warning: automatically enables admin socket) 56 | --web_path string optional path to custom web interface application 57 | 58 | Global Flags: 59 | -h, --help help for 60 | 61 | Use " help [command]" for more information about a command. 62 | ``` 63 | 64 | ### version 65 | 66 | To show version and exit run: 67 | 68 | ``` 69 | centrifugo version 70 | ``` 71 | 72 | ### configuration JSON file example 73 | 74 | But the subject of this section - configuration file. As was mentioned earlier it must be a file with valid JSON. 75 | 76 | Let's look at configuration file example I personally use while developing Centrifugo: 77 | 78 | ```javascript 79 | { 80 | "secret": "secret", 81 | "namespaces": [ 82 | { 83 | "name": "public", 84 | "publish": true, 85 | "watch": true, 86 | "presence": true, 87 | "join_leave": true, 88 | "history_size": 10, 89 | "history_lifetime": 30 90 | } 91 | ], 92 | "log_level": "debug" 93 | } 94 | ``` 95 | 96 | Only **secret** options is required. 97 | 98 | You will know about other options such as `namespaces` in next sections. 99 | 100 | So the **minimal configuration file required** is: 101 | 102 | ```javascript 103 | { 104 | "secret": "secret" 105 | } 106 | ``` 107 | 108 | But use strong secret in production! 109 | 110 | ### TOML 111 | 112 | Centrifugo also supports TOML format for configuration file: 113 | 114 | ``` 115 | centrifugo --config=config.toml 116 | ``` 117 | 118 | Where `config.toml` contains: 119 | 120 | ``` 121 | log_level = "debug" 122 | 123 | secret = "secret" 124 | 125 | [[namespaces]] 126 | name = "public" 127 | publish = true 128 | watch = true 129 | presence = true 130 | join_leave = true 131 | history_size = 10 132 | history_lifetime = 30 133 | ``` 134 | 135 | I.e. the same configuration as JSON file above. 136 | 137 | ### YAML 138 | 139 | And YAML config also supported. `config.yaml`: 140 | 141 | ``` 142 | log_level: debug 143 | 144 | secret: secret 145 | namespaces: 146 | - name: public 147 | publish: true 148 | watch: true 149 | presence: true 150 | join_leave: true 151 | history_size: 10 152 | history_lifetime: 30 153 | ``` 154 | 155 | With YAML remember to use spaces, not tabs when writing configuration file 156 | 157 | ### multiple projects 158 | 159 | Since Centrifugo 1.0.0 multiple projects not supported. 160 | 161 | ### checkconfig 162 | 163 | Centrifugo has special command to check configuration file `checkconfig`: 164 | 165 | ```bash 166 | centrifugo checkconfig --config=config.json 167 | ``` 168 | 169 | If any errors found during validation – program will exit with error message and exit status 1. 170 | 171 | ### genconfig 172 | 173 | Another command is `genconfig`: 174 | 175 | ``` 176 | centrifugo genconfig -c config.json 177 | ``` 178 | 179 | It will generate the simplest configuration file for you automatically. 180 | 181 | ### important command-line options 182 | 183 | In next section we will talk about project settings in detail. But before jumping to it 184 | let's describe some of the most important options you can configure when running Centrifugo: 185 | 186 | * `--address` – bind your Centrifugo to specific interface address (by default `""`) 187 | * `--port` – port to bind Centrifugo to (by default `8000`) 188 | * `--engine` – engine to use - `memory` or `redis` (by default `memory`). Read more about engines in next sections. 189 | * `--web` – path to directory of admin web interface application to serve 190 | * `--name` – give Centrifugo server node a name – this os optional as by default Centrifugo will use hostname 191 | and port number to construct node name. 192 | 193 | There are more command line options – we will talk about some of them later. Note that all command-line options can 194 | be set via configuration file, but command-line options will be more valuable when set than configuration file's options. 195 | See description of [viper](https://github.com/spf13/viper) – to see more details about configuration options priority. 196 | -------------------------------------------------------------------------------- /server/connection_check.md: -------------------------------------------------------------------------------- 1 | # Connection check 2 | 3 | When client connects to Centrifugo with proper connection credentials then this connection 4 | can live forever. This means that even if you banned this user in your web application 5 | he will be able to read messages from channels he already subscribed to. This is not 6 | what we want in some cases. 7 | 8 | Project has special option: `connection_lifetime`. Connection lifetime is `0` by default 9 | i.e. by default connection check mechanism is off. 10 | 11 | When connection lifetime is set to value greater than 0 then this is a time in seconds how 12 | long connection will be valid after successful connect. When connection lifetime expires 13 | javascript browser client will make an AJAX POST request to your web application. By default 14 | this request goes to `/centrifuge/refresh/` url endpoint. You can change it using javascript 15 | client configuration option `refreshEndpoint`. In response your server must return JSON with 16 | connection credentials. For example in python: 17 | 18 | ```python 19 | to_return = { 20 | 'user': "USER ID, 21 | 'timestamp': "CURRENT TIMESTAMP AS INTEGER", 22 | 'info': "ADDITIONAL CONNECTION INFO", 23 | 'token': "TOKEN BASED ON PARAMS ABOVE", 24 | } 25 | return json.dumps(to_return) 26 | ``` 27 | 28 | You must just return the same connection credentials for `user` when rendering page 29 | initially. But with current `timestamp`. Javascript client will then send them to 30 | Centrifugo server and connection will be refreshed for a connection lifetime period. 31 | 32 | If you don't want to refresh connection for this user - just return 403 Forbidden 33 | on refresh request to your web application backend. -------------------------------------------------------------------------------- /server/engines.md: -------------------------------------------------------------------------------- 1 | # Engines 2 | 3 | * [Memory engine](#memory-engine) 4 | * [Redis engine](#redis-engine) 5 | 6 | Engine in Centrifugo is responsible for how to publish message, handle subscriptions, save 7 | or retrieve presence and history data. 8 | 9 | By default Centrifugo uses Memory engine. There is also Redis engine available. 10 | 11 | The obvious difference between them - with Memory engine you can start only one 12 | node of Centrifugo, while Redis engine allows to run several nodes on different 13 | machines and they will be connected via Redis, will know about each other due to 14 | Redis and will also keep history and presence data in Redis instead of Centrifugo 15 | node process memory. 16 | 17 | To set engine you can use `engine` configuration option. Available values are 18 | `memory` and `redis`. Default value is `memory`. 19 | 20 | So to work with Redis engine: 21 | 22 | ``` 23 | centrifugo --config=config.json --engine=redis 24 | ``` 25 | 26 | ### Memory engine 27 | 28 | Supports only one node. Nice choice to start with. Supports all features keeping 29 | everything in Centrifugo node process memory. You don't need to install Redis when 30 | using this engine. 31 | 32 | 33 | ### Redis engine 34 | 35 | Allows scaling Centrifugo nodes to different machines. Nodes will use Redis 36 | as message broker. Redis engine keeps presence and history data in Redis, uses Redis 37 | PUB/SUB for internal nodes communication. Also it allows to enqueue API commands. 38 | 39 | ![scheme](https://raw.githubusercontent.com/centrifugal/documentation/master/assets/images/scheme_redis.png) 40 | 41 | How to publish via Redis engine API listener? Start Centrifugo with Redis engine and 42 | ``--redis_api`` option: 43 | 44 | ```bash 45 | centrifugo --config=config.json --engine=redis --redis_api 46 | ``` 47 | 48 | Note, that starting from Centrifugo 1.6.0 Redis API message format changed. You can find old 49 | format description [here](https://github.com/centrifugal/documentation/blob/2eadd7d3f9991c46c463aff4126f2ea37b17bfad/server/engines.md#redis-engine). Old format deprecated and will be removed in future releases. 50 | 51 | Use Redis client for your favorite language, ex. for Python: 52 | 53 | ```python 54 | import redis 55 | import json 56 | 57 | client = redis.Redis() 58 | 59 | command = { 60 | "method": "publish", 61 | "params": { 62 | "channel": "events", 63 | "data": {"event": "message"} 64 | } 65 | } 66 | 67 | client.rpush("centrifugo.api", json.dumps(command)) 68 | ``` 69 | 70 | [RPUSH](https://redis.io/commands/rpush) Redis command allows to push several messages 71 | into queue in one request. 72 | 73 | Note that we RPUSH messages into `centrifugo.api` - this is a default name of API queue 74 | Centrifugo watches for. Actually this is just a LIST data structure in Redis. 75 | 76 | Again - you don't have response here as you are adding commands into Redis queue 77 | and they will be processed as soon as Centrifugo can. If you need to get response - you 78 | should use HTTP API. 79 | 80 | `publish` is the most usable API command in Centrifugo and Redis API listener was implemented 81 | with primary goal to reduce HTTP overhead when publishing quickly. This can also help using 82 | Centrifugo with other languages for which we don't have HTTP API client yet. 83 | 84 | Several configuration options related to Redis engine: 85 | 86 | Run 87 | 88 | ```bash 89 | centrifugo -h 90 | ``` 91 | 92 | And you will see the following options among the others: 93 | 94 | ```bash 95 | --redis_api enable Redis API listener (Redis engine) 96 | --redis_api_num_shards int Number of shards for redis API queue (Redis engine) 97 | --redis_db string redis database (Redis engine) (default "0") 98 | --redis_host string redis host (Redis engine) (default "127.0.0.1") 99 | --redis_master_name string Name of Redis master Sentinel monitors (Redis engine) 100 | --redis_password string redis auth password (Redis engine) 101 | --redis_pool int Redis pool size (Redis engine) (default 256) 102 | --redis_port string redis port (Redis engine) (default "6379") 103 | --redis_sentinels string Comma separated list of Sentinels (Redis engine) 104 | --redis_url string redis connection URL in format redis://:password@hostname:port/db (Redis engine) 105 | ``` 106 | 107 | Most of these options are clear – `--redis_host`, `--redis_port`, `--redis_password`, `--redis_db`, `--redis_pool` 108 | 109 | `--redis_url` allows to set Redis connection parameters in a form of URL in format `redis://:password@hostname:port/db_number`. 110 | 111 | When `--redis_url` set Centrifugo will use URL instead of values provided in `--redis_host`, 112 | `--redis_port`, `--redis_password`, `--redis_db` options. 113 | 114 | The most advanced option here is `--redis_api_num_shards`. It's new in v1.3.0. This option must be 115 | used in conjunction with `--redis_api`, i.e. it makes sense only when Redis API enabled. It creates 116 | up to N additional shard queues that Centrifugo instance will listen to new API commands. 117 | 118 | For example if you set `--redis_api_num_shards` to five then Centrifugo will listen to following 119 | queues in Redis: 120 | 121 | ``` 122 | centrifugo.api.0 123 | centrifugo.api.1 124 | centrifugo.api.2 125 | centrifugo.api.3 126 | centrifugo.api.4 127 | ``` 128 | 129 | You can push new commands to any of these queues and commands will be received by Centrifugo instance 130 | and processed. Why do we need this? 131 | 132 | As we described above when using `--redis_api` you can publish new messages using RPUSH command 133 | into `centrifugo.api` queue in Redis. This is OK until you have small amount of new messages that 134 | must be published. But what if you have thousands of new messages per second? The solution is to 135 | use these shard queues. You distribute messages over those queues and this allows to increase 136 | throughput of Centrifugo. Note that you must decide on your client side to which queue you are going 137 | to push message. To keep message order in channels it's important to push messages belonging to the 138 | same channel into the same queue. This can be achieved using something like `crc16(CHANNEL_NAME) mod N` 139 | function where N is number of shard queues (i.e. ``--redis_api_num_shards`` option). 140 | 141 | In next chapter we will see how to start several Centrifugo nodes using Redis engine. 142 | -------------------------------------------------------------------------------- /server/migrate.md: -------------------------------------------------------------------------------- 1 | # Migrating from Centrifuge to Centrifugo 0.3.0 2 | 3 | Centrifugo server has several advantages over Centrifuge (original server in Python): 4 | 5 | * performance - thanks to Go language 6 | * single binary file as a release - just download and use Centrifugo 7 | * runs on several cores 8 | * better configuration 9 | * supports handling SIGHUP signal to reload configuration 10 | 11 | It's too hard to support both versions so in future only Centrifugo will get new features 12 | in new releases. 13 | 14 | Here I'll describe how to migrate to Centrifugo from Centrifuge 0.8.0. 15 | 16 | All differences in two command-line option names that changed: 17 | 18 | * `log_file` instead of `log_file_prefix` 19 | * `log_level` instead of `logging` 20 | 21 | And two configuration file specific options: 22 | 23 | * `web_password` instead of `password` 24 | * `web_secret` instead of `cookie_secret` 25 | 26 | Redis list key for redis api also was changed: `centrifugo.api` instead `centrifuge.api` 27 | 28 | And that's all. 29 | 30 | Centrifugo 1.0.0 differs from Centrifuge so you should follow changelog to migrate from Centrifugo 0.3.0 to 1.0.0 31 | -------------------------------------------------------------------------------- /server/recover.md: -------------------------------------------------------------------------------- 1 | # How recover mechanism works 2 | 3 | `recover` option available since v1.2.0 4 | 5 | This option inspired by Last-Event-ID mechanism [from Eventsource protocol](http://www.w3.org/TR/2012/WD-eventsource-20120426/). 6 | 7 | Let's describe motivation. We live in non-ideal world and network connection can 8 | disappear sometimes. While client offline some messages could be sent into channel 9 | and when client goes online those messages are lost. Centrifugo can help to automatically 10 | recover reasonable amount of missed messages. 11 | 12 | There are still cases when Centrifugo can't help – for example if client was disconnected 13 | for a week or tons of messages were sent into channels since last subscription attempt... 14 | In these situations client must recover its state with application frontend/backend code help. 15 | 16 | But when connection disappeared for a short amount of time then Centrifugo can automatically 17 | recover missed messages when client resubscribes on channel. For this purpose client provides 18 | last message ID seen when resubscribing and Centrifugo automatically tries to recover missed 19 | messages from message history. 20 | 21 | Recover option must be used in combination with `history_size` and `history_lifetime`. Both 22 | `history_size` and `history_lifetime` must be reasonably configured and recover turned on for 23 | channel (for all channels or for namespace - you decide). 24 | 25 | Note that sometimes your clients don't need to get missed messages at all. This depends on 26 | nature of real-time messages you publish. For example you really don't need missed messages 27 | in case of frequently updated counter with actual value in message. So developer must think 28 | wisely when he wants to enable this mechanism. For example it can be reasonable to enable 29 | it if you show comments on page in real-time. In this case when your client goes online it 30 | receives all missed comments automatically from Centrifugo message history. 31 | 32 | Also note that there are other possible solutions to get missed messages based on your 33 | application code - you can still manually retrieve message history from Centrifugo or from 34 | your application backend. 35 | 36 | Here I'll describe how `recover` option implemented based on interaction between our javascript 37 | client and Centrifugo server. You don't need to read this to just use `recover` feature 38 | as all logic below encapsulated into centrifuge-js client. 39 | 40 | After client first subscribes on channels it doesn't need any missed messages because he 41 | didn't miss anything yet. So it sends subscription command to Centrifugo which contains 42 | channel and `recover` flag set to `false` to indicate that Centrifugo must not search for 43 | missed messages in message history. 44 | 45 | ``` 46 | { 47 | "channel": "news", 48 | "recover": false 49 | } 50 | ``` 51 | 52 | Centrifugo subscribes client on channel `news` and answers back to client with subscribe response. 53 | That response includes field `last` in response body containing last message ID for channel `news` 54 | that Centrifugo has in message history for channel. 55 | 56 | ``` 57 | { 58 | "last": "last-message-id-for-channel" 59 | } 60 | ``` 61 | 62 | Client saves that last message ID for channel and start listening for new messages in channel `news`. 63 | When new message arrives client get message `uid` value and that value considered as last message 64 | ID for channel `news`. So when client loses network connection and then resubscribes on channel `news` 65 | he provides last seen message ID in subscription request. 66 | 67 | ``` 68 | { 69 | "channel": "news", 70 | "recover": true, 71 | "last": "last-message-id-for-channel" 72 | } 73 | ``` 74 | 75 | Centrifugo receives this subscription request and tries to find all missed messages looking 76 | into message history. And then returns all missed messages found to client in subscription 77 | response body: 78 | 79 | ``` 80 | { 81 | "messages": [message, message...] 82 | } 83 | ``` 84 | 85 | So client processes those missed messages and continues to listen channel `news` for new 86 | published messages. 87 | -------------------------------------------------------------------------------- /server/scaling.md: -------------------------------------------------------------------------------- 1 | Scaling with Redis 2 | ================== 3 | 4 | As you can read before – it's possible to run multiple nodes of Centrifugo server 5 | and load balance clients between them. In this chapter I'll show how to do it. We 6 | will start 3 Centrifugo nodes and all those nodes will be connected over Redis. 7 | 8 | For this purpose we must use Redis engine described in previous chapter. 9 | 10 | First, you should have Redis running. As soon as it's running - we can launch 3 11 | Centrifugo instances. Open your terminal and start first one: 12 | 13 | ``` 14 | centrifugo --config=config.json --port=8000 --engine=redis --redis_host=127.0.0.1 --redis_port=6379 15 | ``` 16 | 17 | If your Redis on the same machine and runs on its default port you can omit `--redis_host` 18 | and `--redis_port` options in command above. 19 | 20 | Then open another terminal and launch another Centrifugo instance: 21 | 22 | ``` 23 | centrifugo --config=config.json --port=8001 --engine=redis --redis_host=127.0.0.1 --redis_port=6379 24 | ``` 25 | 26 | Note that we use another port number (8001) as port 8000 already busy by our first Centrifugo instance. 27 | If you are starting Centrifugo instances on different machines then you most probably can use 28 | the same port number for all instances. 29 | 30 | And let's start third instance: 31 | 32 | ``` 33 | centrifugo --config=config.json --port=8002 --engine=redis --redis_host=127.0.0.1 --redis_port=6379 34 | ``` 35 | 36 | Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to 37 | any of them. You can also send API request to any of those nodes - as all nodes connected over Redis 38 | PUB/SUB message will be delivered to all of them. 39 | 40 | To load balance clients between nodes we use Nginx - you can find its configuration here in 41 | documentation. Note that it's important to route clients that use SockJS transports (except 42 | websocket) to the same node as that node keeps client's session information. 43 | 44 | ## Redis sharding 45 | 46 | Starting from Centrifugo v1.6.0 there is a builtin Redis sharding support. 47 | 48 | This resolves some fears about Redis being bottleneck on some large Centrifugo setups. Redis 49 | is single-threaded server, it's insanely fast but if your Redis approaches 100% CPU usage then 50 | this sharding feature is what can help your application to scale. 51 | 52 | At moment Centrifugo supports simple comma-based approach to configuring Redis shards. Let's just 53 | look on examples. 54 | 55 | To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380: 56 | 57 | ``` 58 | centrifugo --config=config.json --engine=redis --redis_port=6379,6380 59 | ``` 60 | 61 | To start Centrifugo with Redis instances on different hosts: 62 | 63 | ``` 64 | centrifugo --config=config.json --engine=redis --redis_host=192.168.1.34,192.168.1.35 65 | ``` 66 | 67 | If you also need to customize AUTH password, Redis DB number then you can use `--redis_url` option. 68 | 69 | Note, that due to how Redis PUB/SUB work it's not possible (and it's pretty useless anyway) to run 70 | shards in one Redis instances using different Redis DB numbers. 71 | 72 | When sharding enabled Centrifugo will spread channels and history/presence keys over configured 73 | Redis instances using consistent hashing algorithm. At moment we use Jump consistent hash algorithm 74 | (see [paper](https://arxiv.org/pdf/1406.2294.pdf) and [implementation](https://github.com/dgryski/go-jump)) 75 | 76 | If you have any feedback on sharding feature - let us know. 77 | -------------------------------------------------------------------------------- /server/settings.md: -------------------------------------------------------------------------------- 1 | # Important configuration settings 2 | 3 | As I wrote in previous chapter configuration file must have one required option: `secret`. 4 | 5 | The configuration file looks like this: 6 | 7 | ```javascript 8 | { 9 | "secret": "very-long-secret-key" 10 | } 11 | ``` 12 | 13 | **The only two who should know this secret key is Centrifugo itself and your web application 14 | backend**. It is used to generate client connection tokens (more about them later), sign API 15 | requests and sign private channel subscription requests. 16 | 17 | The next available option is `connection_lifetime`: 18 | 19 | `connection_lifetime` – this is a time in seconds for client connection to expire. By default it equals `0` - this means that connection can live forever and will not expire. See more about connection expiration mechanism in special chapter. In most situations you don't have to explicitly set it as no connection expiration is ok for most applications. 20 | 21 | ```javascript 22 | { 23 | "secret": "very-long-secret-key", 24 | "connection_lifetime": 0 25 | } 26 | ``` 27 | 28 | Let's look on options related to channels. Channel is an entity to which clients can subscribe to receive messages published into that channel. Channel is just a string - but several symbols has special meaning - see special chapter to find more information about channels. The following options will affect channel behaviour: 29 | 30 | * `watch` – Centrifugo will additionally publish messages into admin channel (these messages can be visible in web interface `messages tab`). By default `false`. Note that this option must be used carefully in channels with high rate of new messages as admin client can not process all of those messages. Use this option in development or for channels with reasonable message rate. 31 | 32 | * `publish` – allow clients to publish messages into channels directly (from client side). Your application will never receive those messages. In idiomatic case all messages must be published by your application backend using Centrifugo API. But this option can be useful when you want to build something without backend-side validation and saving into database. This option can also be useful for demos and prototyping real-time ideas. Note that client can only publish data into channel after successfully subscribed on it. By default it's `false`. 33 | 34 | * `anonymous` – this option enables anonymous access (with empty user ID in connection parameters). In most situations your application works with authorized users so every user has its own unique id. But if you provide real-time features for public access you may need unauthorized access to some channels. Turn on this option and use empty string as user ID. By default `false`. 35 | 36 | * `presence` – enable/disable presence information. Presence is a structure with clients currently subscribed on channel. By default `false` – i.e. no presence information available for channels. 37 | 38 | * `join_leave` – enable/disable sending join(leave) messages when client subscribes on channel (unsubscribes from channel). By default `false`. 39 | 40 | * `history_size` – history size (amount of messages) for channels. As Centrifugo keeps all history messages in memory it's very important to limit maximum amount of messages in channel history to reasonable minimum. By default history size is `0` - this means that channels will have no history messages at all. As soon as history enabled then `history_size` defines maximum amount of messages that Centrifugo will keep for **each** channel in namespace during history lifetime (see below). 41 | 42 | * `history_lifetime` – interval in seconds how long to keep channel history messages. As all history is storing in memory it is also very important to get rid of old history data for unused (inactive for a long time) channels. By default history lifetime is `0` – this means that channels will have no history messages at all. **So to get history messages you should wisely configure both `history_size` and `history_lifetime` options**. 43 | 44 | * `recover` (**new in v1.2.0**) – boolean option, when enabled Centrifugo will try to recover missed messages published while client was disconnected for some reason (bad internet connection for example). By default `false`. This option must be used in conjunction with reasonably configured message history for channel i.e. `history_size` and `history_lifetime` **must be set** (because Centrifugo uses channel message history to recover messages). Also note that note all real-time events require this feature turned on so think wisely when you need this. See more details about how this option works in [special chapter](recover.md). 45 | 46 | * `history_drop_inactive` (**new in v1.3.0**) – boolean option, allows to drastically reduce resource usage (engine memory usage, messages travelling around) when you use message history for channels. In couple of words when enabled Centrifugo will drop history messages that no one needs. Please, see [issue on Github](https://github.com/centrifugal/centrifugo/issues/50) to get more information about option use case scenario and edge cases it involves. 47 | 48 | Let's look how to set all of these options in config: 49 | 50 | ```javascript 51 | { 52 | "secret": "very-long-secret-key", 53 | "connection_lifetime": 0, 54 | "anonymous": true, 55 | "publish": true, 56 | "watch": true, 57 | "presence": true, 58 | "join_leave": true, 59 | "history_size": 10, 60 | "history_lifetime": 30, 61 | "recover": true 62 | } 63 | ``` 64 | 65 | And the last channel specific option is `namespaces`. `namespaces` are optional and if set must be an array of namespace objects. Namespace allows to configure custom options for channels starting with namespace name. This provides a great control over channel behaviour. 66 | 67 | Namespace has a name and the same channel options (with same defaults) as described above. 68 | 69 | * `name` - unique namespace name (name must must consist of letters, numbers, underscores or hyphens and be more than 2 symbols length i.e. satisfy regexp `^[-a-zA-Z0-9_]{2,}$`). 70 | 71 | If you want to use namespace options for channel - you must include namespace name into 72 | channel name with `:` as separator: 73 | 74 | `public:messages` 75 | 76 | `gossips:messages` 77 | 78 | Where `public` and `gossips` are namespace names from project `namespaces`. 79 | 80 | All things together here is an example of `config.json` which includes registered project with all options set and 2 additional namespaces in it: 81 | 82 | ```javascript 83 | { 84 | "secret": "very-long-secret-key", 85 | "connection_lifetime": 0, 86 | "anonymous": true, 87 | "publish": true, 88 | "watch": true, 89 | "presence": true, 90 | "join_leave": true, 91 | "history_size": 10, 92 | "history_lifetime": 30, 93 | "namespaces": [ 94 | { 95 | "name": "public", 96 | "publish": true, 97 | "presence": true, 98 | "join_leave": true, 99 | "anonymous": true, 100 | "history_size": 10, 101 | "history_lifetime": 30, 102 | "recover": true 103 | }, 104 | { 105 | "name": "gossips", 106 | "watch": true 107 | } 108 | ] 109 | } 110 | ``` 111 | 112 | Channel `news` will use global project options. 113 | 114 | Channel `public:news` will use `public` namespace's options. 115 | 116 | Channel `gossips:news` will use `gossips` namespace's options. 117 | -------------------------------------------------------------------------------- /server/signals.md: -------------------------------------------------------------------------------- 1 | # Signal handling 2 | 3 | This is a chapter about sending operating system signals to Centrifugo. 4 | 5 | ### Channel options and configuration reload. 6 | 7 | Centrifugo can reload channel options and some other configuration options on the fly. 8 | 9 | To reload you must send `HUP` signal to centrifugo process: 10 | 11 | ```bash 12 | kill -HUP 13 | ``` 14 | 15 | Some options can not be reloaded because they are used on Centrifugo start. For example various engine 16 | options, interface and port to listen etc. -------------------------------------------------------------------------------- /server/start.md: -------------------------------------------------------------------------------- 1 | # Install Centrifugo and quick start 2 | 3 | Go is a perfect language - it gives developers an opportunity to have single binary executable file for 4 | application and cross-compile application on all target operating systems for distribution. This means 5 | that all you need to get Centrifugo - [download latest release](https://github.com/centrifugal/centrifugo/releases) for you operating system, unpack it and you 6 | are done! 7 | 8 | Now you can see help information for Centrifugo: 9 | 10 | ``` 11 | ./centrifugo -h 12 | ``` 13 | 14 | Centrifugo server node requires configuration file with secret key. 15 | If you are new to Centrifugo then there is `genconfig` command which can generate minimal required 16 | configuration file for you: 17 | 18 | ```bash 19 | ./centrifugo genconfig 20 | ``` 21 | 22 | It will generate secret key for you automatically and create configuration file `config.json` 23 | in current directory (by default) so you can finally run Centrifugo instance: 24 | 25 | ```bash 26 | ./centrifugo --config=config.json 27 | ``` 28 | 29 | We will talk about configuration in detail in next sections. 30 | 31 | You can also put or symlink `centrifugo` into your `bin` OS directory and run it from anywhere: 32 | 33 | ```bash 34 | centrifugo --config=config.json 35 | ``` 36 | 37 | In production you will need to daemonize Centrifugo. We have prebuilt rpm and deb packages for 38 | most popular Linux distributions and Docker image. See more Deploy section for more info. 39 | -------------------------------------------------------------------------------- /server/stats.md: -------------------------------------------------------------------------------- 1 | # Server stats and metrics. 2 | 3 | When you call `stats` API command you get something like this in response body: 4 | 5 | ```javascript 6 | "body": { 7 | "data": { 8 | "nodes": [ 9 | { 10 | "uid": "8a7cf241-62c3-4ef5-8a20-83fbadb74c6f", 11 | "name": "Alexanders-MacBook-Pro.local_8000", 12 | "started_at": 1480764329, 13 | "metrics": { 14 | "client_api_15_count": 0, 15 | "client_api_15_microseconds_50%ile": 0, 16 | "client_api_15_microseconds_90%ile": 0, 17 | "client_api_15_microseconds_99%ile": 0, 18 | "client_api_15_microseconds_99.99%ile": 0, 19 | "client_api_15_microseconds_max": 0, 20 | "client_api_15_microseconds_mean": 0, 21 | "client_api_15_microseconds_min": 0, 22 | "client_api_1_count": 0, 23 | "client_api_1_microseconds_50%ile": 0, 24 | "client_api_1_microseconds_90%ile": 0, 25 | "client_api_1_microseconds_99%ile": 0, 26 | "client_api_1_microseconds_99.99%ile": 0, 27 | "client_api_1_microseconds_max": 0, 28 | "client_api_1_microseconds_mean": 0, 29 | "client_api_1_microseconds_min": 0, 30 | "client_api_num_requests": 0, 31 | "client_bytes_in": 0, 32 | "client_bytes_out": 0, 33 | "client_num_connect": 0, 34 | "client_num_msg_published": 0, 35 | "client_num_msg_queued": 0, 36 | "client_num_msg_sent": 0, 37 | "client_num_subscribe": 0, 38 | "http_api_15_count": 0, 39 | "http_api_15_microseconds_50%ile": 0, 40 | "http_api_15_microseconds_90%ile": 0, 41 | "http_api_15_microseconds_99%ile": 0, 42 | "http_api_15_microseconds_99.99%ile": 0, 43 | "http_api_15_microseconds_max": 0, 44 | "http_api_15_microseconds_mean": 0, 45 | "http_api_15_microseconds_min": 0, 46 | "http_api_1_count": 0, 47 | "http_api_1_microseconds_50%ile": 0, 48 | "http_api_1_microseconds_90%ile": 0, 49 | "http_api_1_microseconds_99%ile": 0, 50 | "http_api_1_microseconds_99.99%ile": 0, 51 | "http_api_1_microseconds_max": 0, 52 | "http_api_1_microseconds_mean": 0, 53 | "http_api_1_microseconds_min": 0, 54 | "http_api_num_requests": 0, 55 | "http_raw_ws_num_requests": 0, 56 | "http_sockjs_num_requests": 0, 57 | "node_cpu_usage": 0, 58 | "node_memory_sys": 10524920, 59 | "node_num_add_client_conn": 0, 60 | "node_num_add_client_sub": 0, 61 | "node_num_add_presence": 0, 62 | "node_num_admin_msg_published": 0, 63 | "node_num_admin_msg_received": 0, 64 | "node_num_channels": 0, 65 | "node_num_client_msg_published": 0, 66 | "node_num_client_msg_received": 0, 67 | "node_num_clients": 0, 68 | "node_num_control_msg_published": 21, 69 | "node_num_control_msg_received": 21, 70 | "node_num_goroutine": 12, 71 | "node_num_history": 0, 72 | "node_num_join_msg_published": 0, 73 | "node_num_join_msg_received": 0, 74 | "node_num_last_message_id": 0, 75 | "node_num_leave_msg_published": 0, 76 | "node_num_leave_msg_received": 0, 77 | "node_num_presence": 0, 78 | "node_num_remove_client_conn": 0, 79 | "node_num_remove_client_sub": 0, 80 | "node_num_remove_presence": 0, 81 | "node_num_unique_clients": 0, 82 | "node_uptime_seconds": 120, 83 | } 84 | } 85 | ], 86 | "metrics_interval": 60 87 | } 88 | } 89 | ``` 90 | 91 | By default Centrifugo aggregates metrics over 60 seconds period. You can change this begaviour using `node_metrics_interval` configuration option. 92 | 93 | Let's look what else you can see in `stats` response body. 94 | 95 | "nodes" is an array of stats information from every Centrifugo node running. 96 | 97 | Inside every node: 98 | 99 | `uid` – unique id of node 100 | 101 | `name` – name of node 102 | 103 | `started_at` – node start time as UNIX timestamp 104 | 105 | `metrics` is a map of metric values (keys always `string`, values always `integer`) - note that this is a snapshot, it only changes once in `node_metrics_interval` period. 106 | 107 | 108 | Let's describe some metrics in detail: 109 | 110 | `node_memory_sys` – node memory usage in bytes 111 | 112 | `node_cpu_usage` – node cpu usage in percents 113 | 114 | `node_num_goroutine` – number of active goroutines (Go language specific) 115 | 116 | `node_num_clients` – number of connected authorized clients 117 | 118 | `node_num_unique_clients` – number of unique (with different user ID) clients connected 119 | 120 | `node_num_channels` – number of active channels (with one or more subscribers) 121 | 122 | `node_num_client_msg_published` – number of messages published 123 | 124 | `node_uptime_seconds` – seconds passed from time when node started. 125 | 126 | `http_api_num_requests` – number of requests to server HTTP API 127 | 128 | `client_api_num_requests` – number of requests to client API 129 | 130 | `client_bytes_in` – number of bytes coming to client API (bytes sent from clients) 131 | 132 | `client_bytes_out` – number of bytes coming out of client API (bytes sent to clients) 133 | 134 | `client_num_msg_queued` – number of messages put into client queues (including protocol messages, join/leave messages etc) 135 | 136 | `client_num_msg_sent` – number of messages actually sent to client (in normal situation must be equal to `num_msg_queued`) 137 | 138 | There are also HDR histogram metric values: for HTTP API and for client API. They are collected over 1 and 15 interval buckets. 139 | 140 | Buckets rotated every `node_metric_interval` seconds. So By default you see values over 1 minute and 15 minute. 141 | -------------------------------------------------------------------------------- /server/tokens_and_signatures.md: -------------------------------------------------------------------------------- 1 | # Tokens and signatures 2 | 3 | Centrifugo uses HMAC with SHA-256 digest algorithm to create connection token and to sign 4 | various data when communicating with Centrifugo via server or client API. 5 | 6 | In this chapter we will see how to create tokens and signatures for different 7 | actions. If you use Python all functions available in `Cent` library and you 8 | don't need to implement them. This chapter can be useful for developers building 9 | their own library (in other language for example) to communicate with Centrifugo. 10 | 11 | Lets start with connection token. 12 | 13 | ### Client connection token 14 | 15 | When client connects to Centrifuge from browser it should provide several connection 16 | parameters: `user`, `timestamp`, `info` (optional) and `token`. 17 | 18 | We discussed the meaning of parameters in other chapters - here we will see 19 | how to generate a proper token for them. 20 | 21 | What you should do to create client token is take hex digest of HMAC initialized 22 | with Centrifugo server `secret` and feeding it `user`, `timestamp` and optionally `info` 23 | (preserve this order). Optional `info` means that if you don't need it then just omit it 24 | in token generation. 25 | 26 | Let's look at Python code example for this: 27 | 28 | ```python 29 | import six 30 | import hmac 31 | from hashlib import sha256 32 | 33 | def generate_token(secret, user, timestamp, info=""): 34 | sign = hmac.new(six.b(secret), digestmod=sha256) 35 | sign.update(six.b(user)) 36 | sign.update(six.b(timestamp)) 37 | sign.update(six.b(info)) 38 | return sign.hexdigest() 39 | ``` 40 | 41 | We initialize HMAC with secret key and ``sha256`` digest mode and then update 42 | it with user ID, timestamp and info (order is important). Info is an optional arguments and if 43 | no info provided empty string is used by default - so it does not affect resulting token 44 | value. 45 | 46 | **Note that our API clients already have functions to generate client token - you don't 47 | have to implement this yourself until you want to implement it for another language for 48 | which we don't have API client yet** 49 | 50 | 51 | ### Private channel subscription sign 52 | 53 | When client wants to subscribe on private channel Centrifuge js client sends AJAX POST 54 | request to your web application. This request contains `client` ID string and one or multiple 55 | private `channels`. In response you should return an object where channels are keys. 56 | 57 | For example you received request with channels `$one` and `$two`. Then you should return 58 | JSON with something like this in response: 59 | 60 | ```python 61 | { 62 | "$one": { 63 | "info": "{}", 64 | "sign": "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss" 65 | }, 66 | "$two": { 67 | "info": "{}", 68 | "sign": "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss" 69 | } 70 | } 71 | ``` 72 | 73 | Where `info` is additional information about connection for this channel and `sign` is 74 | properly constructed HMAC based on client ID, channel name and info. Lets look at Python code 75 | generating this sign: 76 | 77 | ```python 78 | import six 79 | import hmac 80 | from hashlib import sha256 81 | 82 | def generate_channel_sign(secret, client, channel, info=""): 83 | auth = hmac.new(six.b(secret), digestmod=sha256) 84 | auth.update(six.b(str(client))) 85 | auth.update(six.b(str(channel))) 86 | auth.update(six.b(info)) 87 | return auth.hexdigest() 88 | ``` 89 | 90 | Not so different from generating client token. Note that as with client token - info is already JSON 91 | encoded string. 92 | 93 | **Note that our API clients already have functions to generate private channel sign - you don't 94 | have to implement this yourself until you want to implement it for another language for 95 | which we don't have API client yet** 96 | 97 | 98 | ### HTTP API request sign 99 | 100 | When you use Centrifugo server API you should also provide sign in each request. 101 | 102 | Again, Python code for this: 103 | 104 | ```python 105 | import six 106 | import hmac 107 | from hashlib import sha256 108 | 109 | def generate_api_sign(self, secret, encoded_data): 110 | sign = hmac.new(six.b(secret), digestmod=sha256) 111 | sign.update(six.b(encoded_data)) 112 | return sign.hexdigest() 113 | ``` 114 | 115 | `encoded_data` is already a JSON string with your API commands. See available commands 116 | in server API chapter. 117 | 118 | **Note that our API clients already have functions to generate API sign - you don't 119 | have to implement this yourself until you want to implement it for another language for 120 | which we don't have API client yet** 121 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # Admin web interface 2 | 3 | Admin web interface located in its [own repo](https://github.com/centrifugal/web). This 4 | is ReactJS based single-page application. But Centrifugo server comes with this interface builtin - so in most cases you don't need to download it separately. 5 | 6 | [See demo on Heroku](https://centrifugo.herokuapp.com) (password `demo`) to see it in action. 7 | 8 | ![Admin web interface](https://raw.githubusercontent.com/centrifugal/documentation/master/assets/images/web.gif) 9 | 10 | It can: 11 | 12 | * show current server general information and statistics from server nodes. 13 | * monitor messages published in channels in real-time (`watch` option for channel must be turned on). 14 | * call `publish`, `unsubscribe`, `disconnect`, `history`, `presence`, `channels`, `stats` server API commands. For 15 | `publish` command Ace JSON editor helps to write JSON to send into channel. 16 | 17 | To enable web you must run `centrifugo` with `--web` flag. 18 | 19 | ``` 20 | centrifugo --config=config.json --admin --web 21 | ``` 22 | 23 | `--admin` enables admin websocket endpoint web interface uses. 24 | 25 | `--web` tells Centrifugo that it must serve embedded web interface. 26 | 27 | **Note, that you can use only `--web` option to enable both web interface and admin websocket endpoint. 28 | This is because web interface can't work without admin websocket.** 29 | 30 | Also you must additionally set 2 options in config: `admin_password` and `admin_secret`. 31 | 32 | `config.json` 33 | 34 | ```json 35 | { 36 | ..., 37 | "admin_password": "strong_password_to_log_in", 38 | "admin_secret": "strong_secret_key_to_sign_authorization_token" 39 | } 40 | ``` 41 | 42 | * `admin_password` – this is a password to log into admin web interface 43 | * `admin_secret` - this is a secret key to sign authorization token used to call admin API endpoints. 44 | 45 | Make both strong and keep in secret. 46 | 47 | After setting this in config go to http://localhost:8000 (by default) - and you should see web interface. Although there is `password` based authentication a good advice is to protect web interface by firewall rules in production. Also as web interface uses `/socket` Centrifugo websocket endpoint - you better protect that endpoint by firewall too. 48 | 49 | If you don't want to use embedded web interface you can specify path to your own web interface directory: 50 | 51 | ``` 52 | centrifugo --config=config.json --admin --web --web_path=/path/to/web/app 53 | ``` 54 | 55 | This can be useful if you want to modify official web interface in some way. 56 | 57 | There is also an option to run Centrifugo in insecure admin mode (new in v1.3.0) - in this case you 58 | don't need to set `admin_password` and `admin_secret` in config - if you use web interface you will 59 | be logged in automatically without any password. Note that this is only for development or if you 60 | protected admin websocket endpoint and web interface with firewall rules in production. To start 61 | Centrifugo with web interface in insecure admin mode run: 62 | 63 | ``` 64 | centrifugo --config=config.json --admin --insecure_admin --web 65 | ``` 66 | --------------------------------------------------------------------------------