From 8062da3f8b253ae7f95c00d1bc4a822b52bab1b0 Mon Sep 17 00:00:00 2001 From: Danila Vershinin Date: Tue, 3 Feb 2026 15:55:23 +0800 Subject: [PATCH] docs: add GetPageSpeed APT repository for Ubuntu/Debian Add installation option via GetPageSpeed repository, which provides freely available pre-built dynamic module packages for Debian 12/13 and Ubuntu 20.04/22.04/24.04 (amd64 and arm64). --- README.md | 1447 +++++++++++++++++++++++++++-------------------------- 1 file changed, 724 insertions(+), 723 deletions(-) diff --git a/README.md b/README.md index b0d9fd12..6c1e9f5b 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,11 @@ With a well-tuned OS and network stack on commodity server hardware, expect to h #### Download Packages - [Arch Linux](https://archlinux.org): [nginx-mod-nchan](https://aur.archlinux.org/packages/nginx-mod-nchan/) and [nginx-mainline-mod-nchan](https://aur.archlinux.org/packages/nginx-mainline-mod-nchan/) are available in the Arch User Repository. - Mac OS X: a [homebrew](http://brew.sh) package is available. `brew tap denji/nginx; brew install nginx-full --with-nchan-module` - - [Debian](https://www.debian.org/): A dynamic module build is available in the Debian package repository: [libnginx-mod-nchan](https://packages.debian.org/sid/libnginx-mod-nchan). + - [Debian](https://www.debian.org/): A dynamic module build is available in the Debian package repository: [libnginx-mod-nchan](https://packages.debian.org/sid/libnginx-mod-nchan). Additionally, you can use the pre-built static module packages [nginx-common.deb](https://nchan.io/download/nginx-common.deb) and [nginx-extras.deb](https://nchan.io/download/nginx-extras.deb). Download both and install them with `dpkg -i`, followed by `sudo apt-get -f install`. - [Ubuntu](http://www.ubuntu.com/): [nginx-common.ubuntu.deb](https://nchan.io/download/nginx-common.ubuntu.deb) and [nginx-extras.ubuntu.deb](https://nchan.io/download/nginx-extras.ubuntu.deb). Download both and install them with `dpkg -i`, followed by `sudo apt-get -f install`. Who knows when Ubuntu will add Nchan to their repository?... - - [Fedora](https://fedoraproject.org): Dynamic module builds for Nginx > 1.10.0 are available: [nginx-mod-nchan.x86_64.rpm](https://nchan.io/download/nginx-mod-nchan.x86-64.rpm), [nginx-mod-nchan.src.rpm](https://nchan.io/download/nginx-mod-nchan.src.rpm). + - [Fedora](https://fedoraproject.org): Dynamic module builds for Nginx > 1.10.0 are available: [nginx-mod-nchan.x86_64.rpm](https://nchan.io/download/nginx-mod-nchan.x86-64.rpm), [nginx-mod-nchan.src.rpm](https://nchan.io/download/nginx-mod-nchan.src.rpm). + - **Ubuntu / Debian (GetPageSpeed)**: Pre-built dynamic module packages are freely available from the GetPageSpeed repository for Debian 12/13 and Ubuntu 20.04/22.04/24.04 (amd64 and arm64). See [setup instructions](https://apt-nginx-extras.getpagespeed.com/apt-setup/), then install with `sudo apt-get install nginx nginx-module-nchan`. The module is automatically enabled after installation. - [Heroku](https://heroku.com): A buildpack for compiling Nchan into Nginx is available: [nchan-buildpack](https://github.com/andjosh/nchan-buildpack). A one-click, readily-deployable app is also available: [nchan-heroku](https://github.com/andjosh/nchan-heroku). - A statically compiled binary and associated linux nginx installation files are also [available as a tarball](https://nchan.io/download/nginx-nchan-latest.tar.gz). @@ -69,15 +70,15 @@ Once you've built and installed Nchan, it's very easy to start using. Add two lo ```nginx #... -http { +http { server { #... - + location = /sub { nchan_subscriber; nchan_channel_id $arg_id; } - + location = /pub { nchan_publisher; nchan_channel_id $arg_id; @@ -92,7 +93,7 @@ But Nchan is very flexible and highly configurable. So, of course, it can get a ### Conceptual Overview -The basic unit of most pub/sub solutions is the messaging *channel*. Nchan is no different. Publishers send messages to channels with a certain *channel id*, and subscribers subscribed to those channels receive them. Some number of messages may be buffered for a time in a channel's message buffer before they are deleted. Pretty simple, right? +The basic unit of most pub/sub solutions is the messaging *channel*. Nchan is no different. Publishers send messages to channels with a certain *channel id*, and subscribers subscribed to those channels receive them. Some number of messages may be buffered for a time in a channel's message buffer before they are deleted. Pretty simple, right? Well... the trouble is that nginx configuration does not deal with channels, publishers, and subscribers. Rather, it has several sections for incoming requests to match against *server* and *location* sections. **Nchan configuration directives map servers and locations onto channel publishing and subscribing endpoints**: @@ -100,15 +101,15 @@ Well... the trouble is that nginx configuration does not deal with channels, pub #very basic nchan config worker_processes 5; -http { +http { server { listen 80; - + location = /sub { nchan_subscriber; nchan_channel_id foobar; } - + location = /pub { nchan_publisher; nchan_channel_id foobar; @@ -168,7 +169,7 @@ Metadata can be added to a message when using an HTTP POST request for publishin ### Other Publisher Endpoint Actions -**HTTP `GET`** requests return channel information without publishing a message. The response code is `200` if the channel exists, and `404` otherwise: +**HTTP `GET`** requests return channel information without publishing a message. The response code is `200` if the channel exists, and `404` otherwise: ```console > curl --request POST --data "test message" http://127.0.0.2:80/pub ... @@ -207,20 +208,20 @@ Nchan supports several different kinds of subscribers for receiving messages: [* - ### Long-Polling - The tried-and-true server-push method supported by every browser out there. - Initiated by sending an HTTP `GET` request to a channel subscriber endpoint. - The long-polling subscriber walks through a channel's message queue via the built-in cache mechanism of HTTP clients, namely with the "`Last-Modified`" and "`Etag`" headers. Explicitly, to receive the next message for given a long-poll subscriber response, send a request with the "`If-Modified-Since`" header set to the previous response's "`Last-Modified`" header, and "`If-None-Match`" likewise set to the previous response's "`Etag`" header. - Sending a request without a "`If-Modified-Since`" or "`If-None-Match`" headers returns the oldest message in a channel's message queue, or waits until the next published message, depending on the value of the `nchan_subscriber_first_message` config directive. + The tried-and-true server-push method supported by every browser out there. + Initiated by sending an HTTP `GET` request to a channel subscriber endpoint. + The long-polling subscriber walks through a channel's message queue via the built-in cache mechanism of HTTP clients, namely with the "`Last-Modified`" and "`Etag`" headers. Explicitly, to receive the next message for given a long-poll subscriber response, send a request with the "`If-Modified-Since`" header set to the previous response's "`Last-Modified`" header, and "`If-None-Match`" likewise set to the previous response's "`Etag`" header. + Sending a request without a "`If-Modified-Since`" or "`If-None-Match`" headers returns the oldest message in a channel's message queue, or waits until the next published message, depending on the value of the `nchan_subscriber_first_message` config directive. A message's associated content type, if present, will be sent to this subscriber with the `Content-Type` header. - + - ### Interval-Polling Works just like long-polling, except if the requested message is not yet available, immediately responds with a `304 Not Modified`. Nchan cannot automatically distinguish between long-poll and interval-poll subscriber requests, so long-polling must be disabled for a subscriber location if you wish to use interval-polling. - ### Websocket - Bidirectional communication for web browsers. Part of the [HTML5 spec](http://www.w3.org/TR/2014/REC-html5-20141028/single-page.html). Nchan supports the latest protocol version 13 ([RFC 6455](https://tools.ietf.org/html/rfc6455)). - Initiated by sending a websocket handshake to the desired subscriber endpoint location. + Bidirectional communication for web browsers. Part of the [HTML5 spec](http://www.w3.org/TR/2014/REC-html5-20141028/single-page.html). Nchan supports the latest protocol version 13 ([RFC 6455](https://tools.ietf.org/html/rfc6455)). + Initiated by sending a websocket handshake to the desired subscriber endpoint location. If the websocket connection is closed by the server, the `close` frame will contain the HTTP response code and status line describing the reason for closing the connection. Server-initiated keep-alive pings can be configured with the [`nchan_websocket_ping_interval`](#nchan_websocket_ping_interval) config directive. Messages are delivered to subscribers in `text` websocket frames, except if a message's `content-type` is "`application/octet-stream`" -- then it is delivered in a `binary` frame.
@@ -230,11 +231,11 @@ Nchan supports several different kinds of subscribers for receiving messages: [* content-type: message_content_type \n message_data - + The `content-type:` line may be omitted.
#### Websocket Publisher - Messages published through a websocket connection can be forwarded to an upstream application with the [`nchan_publisher_upstream_request`](#nchan_publisher_upstream_request) config directive. + Messages published through a websocket connection can be forwarded to an upstream application with the [`nchan_publisher_upstream_request`](#nchan_publisher_upstream_request) config directive. Messages published in a binary frame are automatically given the `content-type` "`application/octet-stream`". #### Permessage-deflate Nchan version 1.1.8 and above supports the [permessage-deflate protocol extension](https://tools.ietf.org/html/rfc7692). Messages are deflated once when they are published, and then can be broadcast to any number of compatible websocket subscribers. Message deflation is enabled by setting the [`nchan_deflate_message_for_websocket on;`](#nchan_deflate_message_for_websocket) directive in a publisher location. @@ -242,45 +243,45 @@ Nchan supports several different kinds of subscribers for receiving messages: [* The deflated data is stored alongside the original message in memory, or, if large enough, on disk. This means more [shared memory](#nchan_shared_memory_size) is necessary when using `nchan_deflate_message_for_websocket`.
Deflation parameters (speed, memory use, strategy, etc.), can be tweaked using the [`nchan_permessage_deflate_compression_window`](#nchan_permessage_deflate_compression_window), [`nchan_permessage_deflate_compression_level`](#nchan_permessage_deflate_compression_level), - [`nchan_permessage_deflate_compression_strategy`](#nchan_permessage_deflate_compression_strategy), and + [`nchan_permessage_deflate_compression_strategy`](#nchan_permessage_deflate_compression_strategy), and [`nchan_permessage_deflate_compression_window`](#nchan_permessage_deflate_compression_window) settings.
Nchan also supports the (deprecated) [perframe-deflate extension](https://tools.ietf.org/html/draft-tyoshino-hybi-websocket-perframe-deflate-06) still in use by Safari as `x-webkit-perframe-deflate`.
- + - ### EventSource - Also known as [Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) or SSE, it predates Websockets in the [HTML5 spec](http://www.w3.org/TR/2014/REC-html5-20141028/single-page.html), and is a [very simple protocol](http://www.w3.org/TR/eventsource/#event-stream-interpretation). - Initiated by sending an HTTP `GET` request to a channel subscriber endpoint with the "`Accept: text/event-stream`" header. - Each message `data: ` segment will be prefaced by the message `id: `. - To resume a closed EventSource connection from the last-received message, one *should* start the connection with the "`Last-Event-ID`" header set to the last message's `id`. - Unfortunately, browsers [don't support setting](http://www.w3.org/TR/2011/WD-eventsource-20111020/#concept-event-stream-last-event-id) this header for an `EventSource` object, so by default the last message id is set either from the "`Last-Event-Id`" header or the `last_event_id` url query string argument. - This behavior can be configured via the [`nchan_subscriber_last_message_id`](#nchan_subscriber_last_message_id) config. + Also known as [Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) or SSE, it predates Websockets in the [HTML5 spec](http://www.w3.org/TR/2014/REC-html5-20141028/single-page.html), and is a [very simple protocol](http://www.w3.org/TR/eventsource/#event-stream-interpretation). + Initiated by sending an HTTP `GET` request to a channel subscriber endpoint with the "`Accept: text/event-stream`" header. + Each message `data: ` segment will be prefaced by the message `id: `. + To resume a closed EventSource connection from the last-received message, one *should* start the connection with the "`Last-Event-ID`" header set to the last message's `id`. + Unfortunately, browsers [don't support setting](http://www.w3.org/TR/2011/WD-eventsource-20111020/#concept-event-stream-last-event-id) this header for an `EventSource` object, so by default the last message id is set either from the "`Last-Event-Id`" header or the `last_event_id` url query string argument. + This behavior can be configured via the [`nchan_subscriber_last_message_id`](#nchan_subscriber_last_message_id) config. A message's `content-type` will not be received by an EventSource subscriber, as the protocol makes no provisions for this metadata. - A message's associated `event` type, if present, will be sent to this subscriber with the `event:` line. + A message's associated `event` type, if present, will be sent to this subscriber with the `event:` line. - + - ### HTTP [multipart/mixed](http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html#z0) - The `multipart/mixed` MIMEtype was conceived for emails, but hey, why not use it for HTTP? It's easy to parse and includes metadata with each message. - Initiated by including an `Accept: multipart/mixed` header. - The response headers and the unused "preamble" portion of the response body are sent right away, with the boundary string generated randomly for each subscriber. Each subsequent message will be sent as one part of the multipart message, and will include the message time and tag (`Last-Modified` and `Etag`) as well as the optional `Content-Type` headers. - Each message is terminated with the next multipart message's boundary **without a trailing newline**. While this conforms to the multipart spec, it is unusual as multipart messages are defined as *starting*, rather than ending with a boundary. + The `multipart/mixed` MIMEtype was conceived for emails, but hey, why not use it for HTTP? It's easy to parse and includes metadata with each message. + Initiated by including an `Accept: multipart/mixed` header. + The response headers and the unused "preamble" portion of the response body are sent right away, with the boundary string generated randomly for each subscriber. Each subsequent message will be sent as one part of the multipart message, and will include the message time and tag (`Last-Modified` and `Etag`) as well as the optional `Content-Type` headers. + Each message is terminated with the next multipart message's boundary **without a trailing newline**. While this conforms to the multipart spec, it is unusual as multipart messages are defined as *starting*, rather than ending with a boundary. A message's associated content type, if present, will be sent to this subscriber with the `Content-Type` header. - + - ### HTTP Raw Stream A simple subscription method similar to the [streaming subscriber](https://github.com/wandenberg/nginx-push-stream-module/blob/master/docs/directives/subscribers.textile#push_stream_subscriber) of the [Nginx HTTP Push Stream Module](https://github.com/wandenberg/nginx-push-stream-module). Messages are appended to the response body, separated by a newline or configurable by `nchan_subscriber_http_raw_stream_separator`. - ### HTTP [Chunked Transfer](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1) - This subscription method uses the `chunked` `Transfer-Encoding` to receive messages. - Initiated by explicitly including `chunked` in the [`TE` header](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.39): - `TE: chunked` (or `TE: chunked;q=??` where the qval > 0) - The response headers are sent right away, and each message will be sent as an individual chunk. Note that because a zero-length chunk terminates the transfer, **zero-length messages will not be sent** to the subscriber. + This subscription method uses the `chunked` `Transfer-Encoding` to receive messages. + Initiated by explicitly including `chunked` in the [`TE` header](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.39): + `TE: chunked` (or `TE: chunked;q=??` where the qval > 0) + The response headers are sent right away, and each message will be sent as an individual chunk. Note that because a zero-length chunk terminates the transfer, **zero-length messages will not be sent** to the subscriber. Unlike the other subscriber types, the `chunked` subscriber cannot be used with http/2 because it disallows chunked encoding. -## PubSub Endpoint +## PubSub Endpoint PubSub endpoints are Nginx config *locations* with the [*`nchan_pubsub`*](#nchan_pubsub) directive. @@ -321,7 +322,7 @@ So far the examples have used static channel ids, which is not very useful. In p nchan_subscriber; nchan_channel_id $remote_addr; } - + location /sub_by_querystring { #channel id is the query string parameter chanid # GET /sub/sub_by_querystring?foo=bar&chanid=baz will have the channel id set to 'baz' @@ -400,14 +401,14 @@ Channels can be associated with groups to avoid channel ID conflicts: nchan_channel_group "test"; nchan_channel_id "foo"; } - + location /pubsub { nchan_pubsub; nchan_channel_group "production"; nchan_channel_id "foo"; #same channel id, different channel group. Thus, different channel. } - + location /flexgroup_pubsub { nchan_pubsub; nchan_channel_group $arg_group; @@ -423,13 +424,13 @@ Groups can be used to track aggregate channel usage, as well as set limits on th ```nginx #enable group accounting nchan_channel_group_accounting on; - + location ~ /pubsub/(\w+)$ { nchan_pubsub; nchan_channel_group "limited"; nchan_channel_id $1; } - + location ~ /prelimited_pubsub/(\w+)$ { nchan_pubsub; nchan_channel_group "limited"; @@ -437,7 +438,7 @@ Groups can be used to track aggregate channel usage, as well as set limits on th nchan_group_max_subscribers 100; nchan_group_max_messages_memory 50M; } - + location /group { nchan_channel_group limited; nchan_group_location; @@ -464,7 +465,7 @@ limits: max subscribers: 0 max messages: 0 max messages shared memory: 0 - max messages disk space: 0 + max messages disk space: 0 ``` By default, the data is returned in human-readable plaintext, but can also be formatted as JSON, XML, or YAML: @@ -508,7 +509,7 @@ limits: ``` -Limits are only applied locally, regardless of whether Redis is enabled. +Limits are only applied locally, regardless of whether Redis is enabled. If a publisher or subscriber request exceeds a group limit, Nchan will respond to it with a `403 Forbidden` response. @@ -516,7 +517,7 @@ If a publisher or subscriber request exceeds a group limit, Nchan will respond t ## Hooks and Callbacks - + ### Request Authorization This feature, configured with [`nchan_authorize_request`](#nchan_authorize_request), behaves just like the Nginx [http_auth_request module](http://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request_set). @@ -537,7 +538,7 @@ Consider the configuration: proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Forwarded-For $remote_addr; } - + location ~ /pubsub/auth/(\w+)$ { nchan_channel_id $1; nchan_authorize_request /auth; @@ -546,8 +547,8 @@ Consider the configuration: } ``` -Here, any request to the location `/pubsub/auth/<...>` will need to be authorized by your application (`my_app`). Nginx will generate a `GET /pubsub_authorize` request to the application, with additional headers set by the `proxy_set_header` directives. Note that Nchan-specific variables are available for this authorization request. Once your application receives this request, it should decide whether or not to authorize the subscriber. This can be done based on a forwarded session cookie, IP address, or any set of parameters of your choosing. If authorized, it should respond with an empty `200 OK` response. -All non-`2xx` response codes (such as `403 Forbidden`) are interpreted as authorization failures. In this case, the failing response is proxied to the client. +Here, any request to the location `/pubsub/auth/<...>` will need to be authorized by your application (`my_app`). Nginx will generate a `GET /pubsub_authorize` request to the application, with additional headers set by the `proxy_set_header` directives. Note that Nchan-specific variables are available for this authorization request. Once your application receives this request, it should decide whether or not to authorize the subscriber. This can be done based on a forwarded session cookie, IP address, or any set of parameters of your choosing. If authorized, it should respond with an empty `200 OK` response. +All non-`2xx` response codes (such as `403 Forbidden`) are interpreted as authorization failures. In this case, the failing response is proxied to the client. Note that Websocket and EventSource clients will only try to authorize during the initial handshake request, whereas Long-Poll and Interval-Poll subscribers will need to be authorized each time they request the next message, which may flood your application with too many authorization requests. @@ -556,7 +557,7 @@ Note that Websocket and EventSource clients will only try to authorize during th ### Subscriber Presence Subscribers can notify an application when they have subscribed and unsubscribed to a channel using the [`nchan_subscribe_request`](#nchan_subscribe_request) -and [`nchan_unsubscribe_request`](#nchan_unsubscribe_request) settings. +and [`nchan_unsubscribe_request`](#nchan_unsubscribe_request) settings. These should point to Nginx locations configured to forward requests to an upstream proxy (your application): ```nginx @@ -574,14 +575,14 @@ These should point to Nginx locations configured to forward requests to an upstr proxy_set_header X-Subscriber-Type $nchan_subscriber_type; proxy_set_header X-Channel-Id $nchan_channel_id; proxy_set_header X-Original-URI $request_uri; - } + } location = /upstream/sub { proxy_pass http://127.0.0.1:9292/sub; proxy_set_header X-Subscriber-Type $nchan_subscriber_type; proxy_set_header X-Message-Id $nchan_message_id; proxy_set_header X-Channel-Id $nchan_channel_id; proxy_set_header X-Original-URI $request_uri; - } + } ``` In order for `nchan_unsubscribe_request` to work correctly, the location it points to must have `proxy_ignore_client_abort on;`. Otherwise, suddenly aborted subscribers may not trigger an unsubscribe request. @@ -601,14 +602,14 @@ Messages can be forwarded to an upstream application before being published usin nchan_pubsub; nchan_publisher_upstream_request /upstream_pub; } - + location = /upstream_pub { proxy_pass http://127.0.0.1:9292/pub; proxy_set_header X-Publisher-Type $nchan_publisher_type; proxy_set_header X-Prev-Message-Id $nchan_prev_message_id; proxy_set_header X-Channel-Id $nchan_channel_id; proxy_set_header X-Original-URI $request_uri; - } + } ``` With this configuration, incoming messages are first `POST`ed to `http://127.0.0.1:9292/pub`. The upstream response code determines how publishing will proceed: @@ -632,7 +633,7 @@ This default storage method uses a segment of shared memory to store messages an ### Redis -[Redis](http://redis.io) can be used to add **data persistence** and **horizontal scalability**, **failover** and **high availability** to your Nchan setup. +[Redis](http://redis.io) can be used to add **data persistence** and **horizontal scalability**, **failover** and **high availability** to your Nchan setup. @@ -646,7 +647,7 @@ http { } server { listen 80; - + location ~ /redis_sub/(\w+)$ { nchan_subscriber; nchan_channel_id $1; @@ -658,7 +659,7 @@ http { nchan_channel_id $1; } } -} +} ``` All servers with the above configuration connecting to the same redis server share channel and message data. @@ -683,7 +684,7 @@ http { } server { listen 80; - + location ~ /sub/(\w+)$ { nchan_subscriber; nchan_channel_id $1; @@ -695,7 +696,7 @@ http { nchan_redis_pass redis_cluster; } } -} +} ``` @@ -731,10 +732,10 @@ http { upstream my_redis_cluster { nchan_redis_server 127.0.0.1; } - + server { #[...] - + location ~ /nchan_redis_cluster_stats$ { nchan_redis_upstream_stats my_redis_cluster; } @@ -830,7 +831,7 @@ Note the `/channel_events/...` location has a *special* `nchan_channel_group`, ` Now, say I subscribe to `/channel_events/foo` I will refer to this as the channel events subscriber. -Let's see what this channel events subscriber receives when I publish messages to +Let's see what this channel events subscriber receives when I publish messages to Subscribing to `/pubsub/foo` produces the channel event ``` @@ -852,7 +853,7 @@ Deleting `/pubsub/foo` (with HTTP `DELETE /pubsub/foo`): channel_delete foo ``` -The event string itself is configirable with [nchan_channel_event_string](#nchan_channel_event_string). By default, it is set to `$nchan_channel_event $nchan_channel_id`. +The event string itself is configirable with [nchan_channel_event_string](#nchan_channel_event_string). By default, it is set to `$nchan_channel_event $nchan_channel_id`. This string can use any Nginx and [Nchan variables](/#variables). @@ -905,7 +906,7 @@ Here's what each line means, and how to interpret it: - `nchan_version`: current version of Nchan. Available for version 1.1.5 and above. Additionally, when there is at least one `nchan_stub_status` location, this data is also available [through variables](#variables). - + ## Securing Channels ### Securing Publisher Endpoints @@ -922,11 +923,11 @@ http { nchan_channel_id $1; } } - + server { #available to the world listen 80; - + location ~ /sub/(\w+)$ { nchan_subscriber; nchan_channel_group my_app_group; @@ -943,7 +944,7 @@ Here, the subscriber endpoint is available on a public-facing port 80, and the p server { #available to the world - listen 80; + listen 80; location ~ /pub/(\w+)$ { allow 127.0.0.1; deny all; @@ -957,11 +958,11 @@ Here, only the local IP 127.0.0.1 is allowed to use the publisher location, even ### Keeping a Channel Private -A Channel ID that is meant to be private should be treated with the same care as a session ID token. Considering the above use case of one-channel-per-user, how can we ensure that only the authenticated user, and no one else, is able to access his channel? +A Channel ID that is meant to be private should be treated with the same care as a session ID token. Considering the above use case of one-channel-per-user, how can we ensure that only the authenticated user, and no one else, is able to access his channel? First, if you intend on securing the channel contents, you must use TLS/SSL: -```nginx +```nginx http { server { #available only on localhost @@ -981,13 +982,13 @@ http { } ``` -Now that you have a secure connection between the subscriber client and the server, you don't need to worry about the channel ID or messages being passively intercepted. This is a minimum requirement for secure message delivery, but it is not sufficient. +Now that you have a secure connection between the subscriber client and the server, you don't need to worry about the channel ID or messages being passively intercepted. This is a minimum requirement for secure message delivery, but it is not sufficient. You must also take care to do at least one of the following: - [Generate good, high-entropy Channel IDs](#good-ids). - [Authorize all subscribers with the `nchan_authorize_request` config directive](#request-authorization). - [Authorize subscribers and hide channel IDs with the "`X-Accel-Redirect`" mechanism](#x-accel-redirect). - + #### Good IDs An ID that can be guessed is an ID that can be hijacked. If you are not authenticating subscribers (as described below), a channel ID should be impossible to guess. Use at least 128 bits of entropy to generate a random token, associate it with the authenticated user, and share it only with the user's client. Do not reuse tokens, just as you would not reuse session IDs. @@ -998,19 +999,19 @@ This feature uses the [X-Accel feature](https://www.nginx.com/resources/wiki/sta It allows a subscriber client to be authenticated by your application, and then redirected by nginx internally to a location chosen by your application (such as a publisher or subscriber endpoint). This makes it possible to have securely authenticated clients that are unaware of the channel id they are subscribed to. Consider the following configuration: -```nginx +```nginx upstream upstream_app { server 127.0.0.1:8080; } server { - listen 80; - + listen 80; + location = /sub_upstream { proxy_pass http://upstream_app/subscriber_x_accel_redirect; proxy_set_header X-Forwarded-For $remote_addr; } - + location ~ /sub/internal/(\w+)$ { internal; #this location only accessible for internal nginx redirects nchan_subscriber; @@ -1023,7 +1024,7 @@ server { As commented, `/sub/internal/` is inaccessible from the outside: ```console > curl -v http://127.0.0.1/sub/internal/foo - + < HTTP/1.1 404 Not Found < Server: nginx/1.9.5 < @@ -1037,7 +1038,7 @@ As commented, `/sub/internal/` is inaccessible from the outside: ``` But if a request is made to `/sub_upstream`, it gets forwarded to your application (`my_app`) on port 8080 with the url `/subscriber_x_accel_redirect`. -Note that you can set any forwarded headers here like any [`proxy_pass`](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass) Nginx location, +Note that you can set any forwarded headers here like any [`proxy_pass`](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass) Nginx location, but unlike the case with `nchan_authorize_request`, Nchan-specific variables are not available. Now, your application must be set up to handle the request to `/subscriber_x_accel_redirect`. You should make sure the client is properly authenticated (maybe using a session cookie), and generate an associated channel id. If authentication fails, respond with a normal `403 Forbidden` response. You can also pass extra information about the failure in the response body and headers. @@ -1047,7 +1048,7 @@ This is accomplished by responding with an empty `200 OK` response that includes - `X-Accel-Redirect: /sub/internal/my_channel_id` - `X-Accel-Buffering: no` -In the presence of these headers, Nginx will not forward your app's response to the client, and instead will *internally* redirect to `/sub/internal/my_channel_id`. +In the presence of these headers, Nginx will not forward your app's response to the client, and instead will *internally* redirect to `/sub/internal/my_channel_id`. This will behave as if the client had requested the subscriber endpoint location directly. Thus using X-Accel-Redirect it is possible to both authenticate all subscribers *and* keep channel IDs completely hidden from subscribers. @@ -1072,673 +1073,673 @@ location /pub/user { ``` A request to `/sub/foo?userid=1234` will subscribe to channels "shared_foo" and "user_1234" via a multiplexed connection. If you later send a `DELETE` request to `/pub/user?userid=1234`, this subscriber will be disconnected and therefore unsubscribed from both "user_1234" and "shared_foo". - + ## Variables Nchan makes several variables usabled in the config file: - -- `$nchan_channel_id` + +- `$nchan_channel_id` The channel id extracted from a publisher or subscriber location request. For multiplexed locations, this is the first channel id in the list. -- `$nchan_channel_id1`, `$nchan_channel_id2`, `$nchan_channel_id3`, `$nchan_channel_id4` +- `$nchan_channel_id1`, `$nchan_channel_id2`, `$nchan_channel_id3`, `$nchan_channel_id4` As above, but for the nth channel id in multiplexed channels. -- `$nchan_subscriber_type` +- `$nchan_subscriber_type` For subscriber locations, this variable is set to the subscriber type (websocket, longpoll, etc.). -- `$nchan_channel_subscriber_last_seen` +- `$nchan_channel_subscriber_last_seen` For publisher locations, this variable is set to the timestamp for the last connected subscriber. - -- `$nchan_channel_subscriber_count` + +- `$nchan_channel_subscriber_count` For publisher locations, this variable is set to the number of subscribers in the published channel. - -- `$nchan_channel_message_count` + +- `$nchan_channel_message_count` For publisher locations, this variable is set to the number of messages buffered in the published channel. - -- `$nchan_publisher_type` + +- `$nchan_publisher_type` For publisher locations, this variable is set to the subscriber type (http or websocket). - -- `$nchan_prev_message_id`, `$nchan_message_id` + +- `$nchan_prev_message_id`, `$nchan_message_id` The current and previous (if applicable) message id for publisher request or subscriber response. -- `$nchan_channel_event` +- `$nchan_channel_event` For channel events, this is the event name. Useful when configuring `nchan_channel_event_string`. -- `$nchan_version` +- `$nchan_version` Current Nchan version. Available since 1.1.5. - + Additionally, `nchan_stub_status` data is also exposed as variables. These are available only when `nchan_stub_status` is enabled on at least one location: -- `$nchan_stub_status_total_published_messages` -- `$nchan_stub_status_stored_messages` -- `$nchan_stub_status_shared_memory_used` -- `$nchan_stub_status_channels` -- `$nchan_stub_status_subscribers` -- `$nchan_stub_status_redis_pending_commands` -- `$nchan_stub_status_redis_connected_servers` -- `$nchan_stub_status_redis_unhealthy_upstreams` -- `$nchan_stub_status_total_ipc_alerts_received` -- `$nchan_stub_status_ipc_queued_alerts` -- `$nchan_stub_status_total_ipc_send_delay` -- `$nchan_stub_status_total_ipc_receive_delay` +- `$nchan_stub_status_total_published_messages` +- `$nchan_stub_status_stored_messages` +- `$nchan_stub_status_shared_memory_used` +- `$nchan_stub_status_channels` +- `$nchan_stub_status_subscribers` +- `$nchan_stub_status_redis_pending_commands` +- `$nchan_stub_status_redis_connected_servers` +- `$nchan_stub_status_redis_unhealthy_upstreams` +- `$nchan_stub_status_total_ipc_alerts_received` +- `$nchan_stub_status_ipc_queued_alerts` +- `$nchan_stub_status_total_ipc_send_delay` +- `$nchan_stub_status_total_ipc_receive_delay` ## Configuration Directives -- **nchan_channel_id** - arguments: 1 - 7 - default: `(none)` - context: server, location, if - > Channel id for a publisher or subscriber location. Can have up to 4 values to subscribe to up to 4 channels. - [more details](#the-channel-id) - -- **nchan_channel_id_split_delimiter** - arguments: 1 - default: `(none)` - context: server, location, if - > Split the channel id into several ids for multiplexing using the delimiter string provided. - [more details](#channel-multiplexing) - -- **nchan_deflate_message_for_websocket** `[ on | off ]` - arguments: 1 - default: `off` - context: server, location - > Store a compressed (deflated) copy of the message along with the original to be sent to websocket clients supporting the permessage-deflate protocol extension - -- **nchan_eventsource_event** - arguments: 1 - default: `(none)` - context: server, location, if - > Set the EventSource `event:` line to this value. When used in a publisher location, overrides the published message's `X-EventSource-Event` header and associates the message with the given value. When used in a subscriber location, overrides all messages' associated `event:` string with the given value. - -- **nchan_eventsource_ping_comment** - arguments: 1 - default: `(empty)` - context: server, location, if - > Set the EventSource comment `: ...` line for periodic pings from server to client. Newlines are not allowed. If empty, no comment is sent with the ping. - -- **nchan_eventsource_ping_data** - arguments: 1 - default: `(empty)` - context: server, location, if - > Set the EventSource `data:` line for periodic pings from server to client. Newlines are not allowed. If empty, no data is sent with the ping. - -- **nchan_eventsource_ping_event** - arguments: 1 - default: `ping` - context: server, location, if - > Set the EventSource `event:` line for periodic pings from server to client. Newlines are not allowed. If empty, no event type is sent with the ping. - -- **nchan_eventsource_ping_interval** ` (seconds)` - arguments: 1 - default: `0 (none)` - context: server, location, if - > Interval for sending ping messages to EventSource subscribers. Disabled by default. - -- **nchan_longpoll_multipart_response** `[ off | on | raw ]` - arguments: 1 - default: `off` - context: server, location, if - > when set to 'on', enable sending multiple messages in a single longpoll response, separated using the multipart/mixed content-type scheme. If there is only one available message in response to a long-poll request, it is sent unmodified. This is useful for high-latency long-polling connections as a way to minimize round-trips to the server. When set to 'raw', sends multiple messages using the http-raw-stream message separator. - -- **nchan_permessage_deflate_compression_level** `[ 0-9 ]` - arguments: 1 - default: `6` - context: http - > Compression level for the `deflate` algorithm used in websocket's permessage-deflate extension. 0: no compression, 1: fastest, worst, 9: slowest, best - -- **nchan_permessage_deflate_compression_memlevel** `[ 1-9 ]` - arguments: 1 - default: `8` - context: http - > Memory level for the `deflate` algorithm used in websocket's permessage-deflate extension. How much memory should be allocated for the internal compression state. 1 - minimum memory, slow and reduces compression ratio; 9 - maximum memory for optimal speed - -- **nchan_permessage_deflate_compression_strategy** `[ default | filtered | huffman-only | rle | fixed ]` - arguments: 1 - default: `default` - context: http - > Compression strategy for the `deflate` algorithm used in websocket's permessage-deflate extension. Use 'default' for normal data, For details see [zlib's section on copression strategies](http://zlib.net/manual.html#Advanced) - -- **nchan_permessage_deflate_compression_window** `[ 9-15 ]` - arguments: 1 - default: `10` - context: http - > Compression window for the `deflate` algorithm used in websocket's permessage-deflate extension. The base two logarithm of the window size (the size of the history buffer). The bigger the window, the better the compression, but the more memory used by the compressor. - -- **nchan_publisher** `[ http | websocket ]` - arguments: 0 - 2 - default: `http websocket` - context: server, location, if - legacy name: push_publisher - > Defines a server or location as a publisher endpoint. Requests to a publisher location are treated as messages to be sent to subscribers. See the protocol documentation for a detailed description. - [more details](#publisher-endpoints) - -- **nchan_publisher_channel_id** - arguments: 1 - 7 - default: `(none)` - context: server, location, if - > Channel id for publisher location. - -- **nchan_publisher_upstream_request** `` - arguments: 1 - context: server, location, if - > Send POST request to internal location (which may proxy to an upstream server) with published message in the request body. Useful for bridging websocket publishers with HTTP applications, or for transforming message via upstream application before publishing to a channel. - > The upstream response code determines how publishing will proceed. A `200 OK` will publish the message from the upstream response's body. A `304 Not Modified` will publish the message as it was received from the publisher. A `204 No Content` will result in the message not being published. - [more details](#message-forwarding) - -- **nchan_pubsub** `[ http | websocket | eventsource | longpoll | intervalpoll | chunked | multipart-mixed | http-raw-stream ]` - arguments: 0 - 6 - default: `http websocket eventsource longpoll chunked multipart-mixed` - context: server, location, if - > Defines a server or location as a pubsub endpoint. For long-polling, GETs subscribe. and POSTs publish. For Websockets, publishing data on a connection does not yield a channel metadata response. Without additional configuration, this turns a location into an echo server. - [more details](#pubsub-endpoint) - -- **nchan_subscribe_request** `` - arguments: 1 - context: server, location, if - > Send GET request to internal location (which may proxy to an upstream server) after subscribing. Disabled for longpoll and interval-polling subscribers. - [more details](#subscriber-presence) - -- **nchan_subscriber** `[ websocket | eventsource | longpoll | intervalpoll | chunked | multipart-mixed | http-raw-stream ]` - arguments: 0 - 5 - default: `websocket eventsource longpoll chunked multipart-mixed` - context: server, location, if - legacy name: push_subscriber - > Defines a server or location as a channel subscriber endpoint. This location represents a subscriber's interface to a channel's message queue. The queue is traversed automatically, starting at the position defined by the `nchan_subscriber_first_message` setting. - > The value is a list of permitted subscriber types. - [more details](#subscriber-endpoints) - -- **nchan_subscriber_channel_id** - arguments: 1 - 7 - default: `(none)` - context: server, location, if - > Channel id for subscriber location. Can have up to 4 values to subscribe to up to 4 channels. - -- **nchan_subscriber_compound_etag_message_id** - arguments: 1 - default: `off` - context: server, location, if - > Override the default behavior of using both `Last-Modified` and `Etag` headers for the message id. - > Enabling this option packs the entire message id into the `Etag` header, and discards - > `Last-Modified` and `If-Modified-Since` headers. - [more details]() - -- **nchan_subscriber_first_message** `[ oldest | newest | ]` - arguments: 1 - default: `oldest` - context: server, location, if - > Controls the first message received by a new subscriber. 'oldest' starts at the oldest available message in a channel's message queue, 'newest' waits until a message arrives. If a number `n` is specified, starts at `n`th message from the oldest. (`-n` starts at `n`th from now). 0 is equivalent to 'newest'. - -- **nchan_subscriber_http_raw_stream_separator** `` - arguments: 1 - default: `\n` - context: server, location, if - > Message separator string for the http-raw-stream subscriber. Automatically terminated with a newline character if not explicitly set to an empty string. - -- **nchan_subscriber_info** - arguments: 0 - context: location - > A subscriber location for debugging the state of subscribers on a given channel. The subscribers of the channel specified by `nchan_channel_id` evaluate `nchan_subscriber_info_string` and send it back to the requested on this location. This is useful to see where subscribers are in an Nchan cluster, as well as debugging subscriber connection issues. - -- **nchan_subscriber_info_string** - arguments: 1 - default: `$nchan_subscriber_type $remote_addr:$remote_port $http_user_agent $server_name $request_uri $pid` - context: server, location - > this string is evaluated by each subscriber on a given channel and sent to the requester of a `nchan_subscriber_info` location - -- **nchan_subscriber_last_message_id** - arguments: 1 - 5 - default: `$http_last_event_id $arg_last_event_id` - context: server, location, if - > If `If-Modified-Since` and `If-None-Match` headers are absent, set the message id to the first non-empty of these values. Used primarily as a workaround for the inability to set the first `Last-Message-Id` of a web browser's EventSource object. - -- **nchan_subscriber_message_id_custom_etag_header** - arguments: 1 - default: `(none)` - context: server, location, if - > Use a custom header instead of the Etag header for message ID in subscriber responses. This setting is a hack, useful when behind a caching proxy such as Cloudflare that under some conditions (like using gzip encoding) swallow the Etag header. - -- **nchan_subscriber_timeout** ` (seconds)` - arguments: 1 - default: `0 (none)` - context: http, server, location, if - legacy name: push_subscriber_timeout - > Maximum time a subscriber may wait for a message before being disconnected. If you don't want a subscriber's connection to timeout, set this to 0. When possible, the subscriber will get a response with a `408 Request Timeout` status; otherwise the subscriber will simply be disconnected. - -- **nchan_unsubscribe_request** `` - arguments: 1 - context: server, location, if - > Send GET request to internal location (which may proxy to an upstream server) after unsubscribing. Disabled for longpoll and interval-polling subscribers. - [more details](#subscriber-presence) - -- **nchan_websocket_client_heartbeat** ` ` - arguments: 2 - default: `none (disabled)` - context: server, location, if - > Most browser Websocket clients do not allow manually sending PINGs to the server. To overcome this limitation, this setting can be used to set up a PING/PONG message/response connection heartbeat. When the client sends the server message *heartbeat_in* (PING), the server automatically responds with *heartbeat_out* (PONG). - -- **nchan_websocket_ping_interval** ` (seconds)` - arguments: 1 - default: `0 (none)` - context: server, location, if - > Interval for sending websocket ping frames. Disabled by default. - -- **nchan_access_control_allow_credentials** - arguments: 1 - default: `on` - context: http, server, location, if - > When enabled, sets the [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) `Access-Control-Allow-Credentials` header to `true`. - -- **nchan_access_control_allow_origin** `` - arguments: 1 - default: `$http_origin` - context: http, server, location, if - > Set the [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) `Access-Control-Allow-Origin` header to this value. If the incoming request's `Origin` header does not match this value, respond with a `403 Forbidden`. Multiple origins can be provided in a single argument separated with a space. - -- **nchan_authorize_request** `` - arguments: 1 - context: server, location, if - > Send GET request to internal location (which may proxy to an upstream server) for authorization of a publisher or subscriber request. A 200 response authorizes the request, a 403 response forbids it. - [more details](#request-authorization) - -- **nchan_channel_group** `` - arguments: 1 - default: `(none)` - context: server, location, if - legacy name: push_channel_group - > The accounting and security group a channel belongs to. Works like a prefix string to the channel id. Can be set with nginx variables. - -- **nchan_channel_group_accounting** - arguments: 1 - default: `off` - context: server, location - > Enable tracking channel, subscriber, and message information on a per-channel-group basis. Can be used to place upper limits on channel groups. - -- **nchan_group_location** `[ get | set | delete | off ]` - arguments: 0 - 3 - default: `get set delete` - context: location - > Group information and configuration location. GET request for group info, POST to set limits, DELETE to delete all channels in group. - -- **nchan_group_max_channels** `` - arguments: 1 - default: `0 (unlimited)` - context: location - > Maximum number of channels allowed in the group. - -- **nchan_group_max_messages** `` - arguments: 1 - default: `0 (unlimited)` - context: location - > Maximum number of messages allowed for all the channels in the group. - -- **nchan_group_max_messages_disk** `` - arguments: 1 - default: `0 (unlimited)` - context: location - > Maximum amount of disk space allowed for the messages of all the channels in the group. - -- **nchan_group_max_messages_memory** `` - arguments: 1 - default: `0 (unlimited)` - context: location - > Maximum amount of shared memory allowed for the messages of all the channels in the group. - -- **nchan_group_max_subscribers** `` - arguments: 1 - default: `0 (unlimited)` - context: location - > Maximum number of subscribers allowed for the messages of all the channels in the group. - -- **nchan_max_channel_id_length** `` - arguments: 1 - default: `1024` - context: http, server, location - legacy name: push_max_channel_id_length - > Maximum permissible channel id length (number of characters). This settings applies to ids before they may be split by the `nchan_channel_id_split_delimiter` Requests with a channel id that is too long will receive a `403 Forbidden` response. - -- **nchan_max_channel_subscribers** `` - arguments: 1 - default: `0 (unlimited)` - context: http, server, location - legacy name: push_max_channel_subscribers - > Maximum concurrent subscribers to the channel on this Nchan server. Does not include subscribers on other Nchan instances when using a shared Redis server. - -- **nchan_subscribe_existing_channels_only** `[ on | off ]` - arguments: 1 - default: `off` - context: http, server, location - legacy name: push_authorized_channels_only - > Whether or not a subscriber may create a channel by sending a request to a subscriber location. If set to on, a publisher must send a POST or PUT request before a subscriber can request messages on the channel. Otherwise, all subscriber requests to nonexistent channels will get a 403 Forbidden response. - -- **nchan_message_buffer_length** `[ | ]` - arguments: 1 - default: `10` - context: http, server, location - legacy names: push_max_message_buffer_length, push_message_buffer_length - > Publisher configuration setting the maximum number of messages to store per channel. A channel's message buffer will retain a maximum of this many most recent messages. An Nginx variable can also be used to set the buffer length dynamically. - -- **nchan_message_temp_path** `` - arguments: 1 - default: `` - context: http - > Large messages are stored in temporary files in the `client_body_temp_path` or the `nchan_message_temp_path` if the former is unavailable. Default is the built-in default `client_body_temp_path` - -- **nchan_message_timeout** `[