├── 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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------