Skip to content

Proxy calls to app backend

Starting from Centrifugo v2.3.0 it's possible to proxy some client connection events over HTTP to application backend and react to them in a way you need. For example you can authenticate connection via request from Centrifugo to your app backend, refresh client sessions and answer to RPC calls sent by client over WebSocket or SockJS connections.

Proxy request structure

All proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with configured timeout. These requests will have some headers copied from original client request (see details below) and include JSON body which varies depending on call type (for example data sent by client in RPC call etc, see more details about JSON bodies below).

Proxy headers

By default the following headers from original client request will be copied to proxied request:

  • Origin (Centrifugo >= v2.3.1)
  • User-Agent
  • Cookie
  • Authorization
  • X-Real-Ip
  • X-Forwarded-For
  • X-Request-Id

It's possible to add extra headers using proxy_extra_http_headers configuration option (available since v2.3.1). This is an array of strings in configuration file, ex:

{
  ...
  "proxy_extra_http_headers": ["X-B3-TraceId", "X-B3-SpanId"]
}

Alternatively you can set extra headers via environment variable (space separated):

export CENTRIFUGO_PROXY_EXTRA_HTTP_HEADERS="X-B3-TraceId X-B3-SpanId"
./centrifugo

Since v2.5.1 Centrifugo also forces Content-Type header to be application/json in all proxy HTTP requests since it sends body in JSON format to application backend.

connect proxy

With the following options in configuration file:

{
  ...
  "proxy_connect_endpoint": "http://localhost:3000/centrifugo/connect",
  "proxy_connect_timeout":  1
}

– connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side you can authenticate incoming connection and return client credentials to Centrifugo in response to proxied request.

This means you don't need to generate JWT token and pass it to client side and can rely on cookie while authenticating user. Centrifugo should work on same domain in this case so your site cookie could be passed to Centrifugo. This also means that every new connection from user will result in HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests generated during reconnect process (until JWT expired).

Payload example that will be sent to app backend when client without token wants to establish connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string:

{
  "client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
  "transport":"websocket",
  "protocol": "json",
  "encoding":"json"
}

Request fields:

  • client is a unique client ID generated by Centrifugo for each incoming connection
  • transport is transport name (websocket or sockjs at moment)
  • protocol is a protocol type used by client (json or protobuf at moment)
  • encoding is a protocol encoding type used (json or binary at moment)

Response expected:

{"result": {"user": "56"}}

Result fields you can set:

  • user (string) is user ID (calculated on app backend based on request cookie header for example). Return it as empty string for accepting unauthenticated request
  • expire_at (optional integer) is a timestamp when connection must be considered expired. If not set or set to 0 connection won't expire at all
  • info (optional JSON) is a connection info JSON
  • b64info (optional string) is a binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages
  • data (optional JSON) is a custom data to send to client in connect command response. Supported since v2.3.1
  • b64data (optional string) is a custom data to send to client in connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client. Supported since v2.3.1
  • channels (optional array of strings) - allows to provide a list of server-side channels to subscribe connection to. See more details about server-side subscriptions. Supported since v2.4.0

proxy_connect_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.

Here is the simplest example of connect handler in Tornado Python framework (note that in real system you need to authenticate user on your backend side, here we just return "56" as user ID):

class CentrifugoConnectHandler(tornado.web.RequestHandler):

    def check_xsrf_cookie(self):
        pass

    def post(self):
        self.set_header('Content-Type', 'application/json; charset="utf-8"')
        data = json.dumps({
            'result': {
                'user': '56'
            }
        })
        self.write(data)


def main():
    options.parse_command_line()
    app = tornado.web.Application([
      (r'/centrifugo/connect', CentrifugoConnectHandler),
    ])
    app.listen(3000)
    tornado.ioloop.IOLoop.instance().start()


if __name__ == '__main__':
    main()

This example should help you to implement similar HTTP handler in any language/framework you are using on backend side.

refresh proxy

With the following options in configuration file:

{
  ...
  "proxy_refresh_endpoint": "http://localhost:3000/centrifugo/refresh",
  "proxy_refresh_timeout":  1
}

– Centrifugo will call proxy_refresh_endpoint when it's time to refresh connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on client side.

Payload sent to app backend in refresh request (when connection is going to expire):

{
  "client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
  "transport":"websocket",
  "protocol": "json",
  "encoding":"json",
  "user":"56"
}

Request fields:

  • client, transport, protocol, encoding are the same as described for connect request payload
  • user is a connection user ID obtained during authentication process

Response expected:

{"result": {"expire_at": 1565436268}}

Result fields:

  • expired (boolean) is a flag to mark connection as expired - client will be diconnected
  • expire_at (integer) is a next timestamp when connection must be considered expired
  • info (optional JSON) is a connection info JSON
  • b64info (optional string) is a binary connection info encoded in base64 format

proxy_refresh_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.

rpc proxy

With the following option in configuration file:

{
  ...
  "proxy_rpc_endpoint": "http://localhost:3000/centrifugo/connect",
  "proxy_rpc_timeout":  1
}

RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows developer to utilize WebSocket (or SockJS) connection in bidirectional way.

Payload sent to app backend in RPC request:

{
  "client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
  "transport":"websocket",
  "protocol": "json",
  "encoding":"json",
  "user":"56",
  "data":{"method":"getCurrentYear"}
}

Request fields:

  • client, transport, protocol, encoding are the same as described for connect request payload
  • user is a connection user ID obtained during authentication process
  • data is an RPC data sent by client
  • b64data will be set instead of data field for connections that use binary payload encoding

Response expected:

{"result": {"data": {"answer": "2019"}}}

Result fields:

  • data (JSON) is and RPC response - any valid JSON is supported
  • b64data string can be set instead of data for binary response encoded in base64 format

proxy_rpc_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.

Return custom error

Application backend can return JSON object that contain an error to return it to client:

{
  "error": {
    "code": 1000,
    "message": "custom error"
  }
}

Application can use error codes >= 1000, error codes in range 0-999 are reserved by Centrifugo internal protocol.

This does not apply to response on refresh request as there is no sense in returning an error (will not reach client anyway).

proxy_rpc_timeout (float, in seconds) controls timeout of HTTP POST request sent to app backend.

Return custom disconnect

Application backend can return JSON object that contain an custom disconnect object to disconnect client in custom way:

{
  "disconnect": {
    "code": 4000,
    "reconnect": false,
    "reason": "custom disconnect"
  }
}

Application can use numbers in range 4000-4999 for custom disconnect codes. Numbers below 4000 are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 symbols.

This does not apply to response on refresh request as there is no way to control disconnect at moment - client will always be disconnected with expired disconnect reason.