github.com/anycable/anycable-go@v1.5.1/docs/sse.md (about) 1 # Server-sent events 2 3 In addition to WebSockets and [long polling](./long_polling.md)), AnyCable also allows you to use [Server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) (SSE) as a transport for receiving live updates. 4 5 SSE is supported by all modern browsers (see [caniuse](https://caniuse.com/eventsource)) and is a good alternative to WebSockets if you don't need to send messages from the client to the server and don't want to deal with Action Cable or AnyCable SDKs: you can use native browsers `EventSource` API to establish a reliable connection or set up HTTP streaming manually using your tool of choice (e.g., `fetch` in a browser, `curl` in a terminal, etc.). 6 7 ## Configuration 8 9 You must opt-in to enable SSE support in AnyCable. To do so, you must provide the `--sse` or set the `ANYCABLE_SSE` environment variable to `true`: 10 11 ```sh 12 $ anycable-go --sse 13 14 INFO 2023-09-06T22:52:04.229Z context=main Starting GoBenchCable 1.4.4 (with mruby 1.2.0 (2015-11-17)) (pid: 39193, open file limit: 122880, gomaxprocs: 8) 15 ... 16 INFO 2023-09-06T22:52:04.229Z context=main Handle WebSocket connections at http://localhost:8080/cable 17 INFO 2023-09-06T22:52:04.229Z context=main Handle SSE requests at http://localhost:8080/events 18 ... 19 ``` 20 21 The default path for SSE connections is `/events`, but you can configure it via the `--sse_path` configuration option. 22 23 ## Usage with EventSource 24 25 The easiest way to use SSE is to use the native `EventSource` API. For that, you MUST provide a URL to the SSE endpoint and pass the channel information in query parameters (EventSource only supports GET requests, so we cannot use the body). For example: 26 27 ```js 28 const source = new EventSource("http://localhost:8080/events?channel=ChatChannel"); 29 30 // Setup an event listener to handle incoming messages 31 source.addEventListener("message", (e) => { 32 // e.data contains the message payload as a JSON string, so we need to parse it 33 console.log(JSON.parse(e.data)); 34 }); 35 ``` 36 37 The snippet above will establish a connection to the SSE endpoint and subscribe to the `ChatChannel` channel. 38 39 If you need to subscribe to a channel with parameters, you MUST provide the fully qualified channel identifier via the `identifier` query parameter: 40 41 ```js 42 const identifier = JSON.stringify({ 43 channel: "BenchmarkChannel", 44 room_id: 42, 45 }); 46 47 const source = new EventSource( 48 `http://localhost:8080/events?identifier=${encodeURIComponent(identifier)}` 49 ); 50 51 // ... 52 ``` 53 54 **IMPORTANT**: You MUST specify either `channel` or `identifier` query parameters. If you don't, the connection will be rejected. 55 56 ### Usage with signed/public streams 57 58 > @since v1.5.1 59 60 When using with [signed streams](./signed_streams.md), you can provide the public or signed stream name via the `stream` or `signed_stream` parameter respectively: 61 62 ```js 63 const publicSource = new EventSource( 64 `http://localhost:8080/events?stream=${encodeURIComponent(myStreamName)}` 65 ); 66 67 const signedSource = new EventSource( 68 `http://localhost:8080/events?signed_stream=${encodeURIComponent(mySecretStreamName)}` 69 ); 70 ``` 71 72 ### Reliability 73 74 EventSource is a reliable transport, which means that it will automatically reconnect if the connection is lost. 75 76 EventSource also keeps track of received messages and sends the last consumed ID on reconnection. To leverage this feature, you MUST enable AnyCable [reliable streams](./reliable_streams.md) functionality. No additional client-side configuration is required. 77 78 **IMPORTANT**: EventSource is assumed to be used with a single stream of data. If you subscribe a client to multiple Action Cable streams (e.g., multiple `stream_from` calls), the last consumed ID will be sent only for the last observed stream. 79 80 ### Requesting initial history 81 82 > @since v1.5.1 83 84 You can also specify the timestamp (Unix seconds) from which request initial stream history (if any): 85 86 ```js 87 // Last 5 minutes 88 const ts = ((Date.now() / 1000) - 5*60) | 0; 89 90 const publicSourceWithHistory = new EventSource( 91 `http://localhost:8080/events?stream=${encodeURIComponent(myStreamName)}&history_since=${ts}` 92 ); 93 ``` 94 95 ### Unauthorized connections or rejected subscriptions 96 97 If the connection is unauthorized or the subscription is rejected, the server will respond with a `401` status code and close the connection. EventSource will automatically reconnect after a short delay. Please, make sure you handle `error` events and close the connection if you don't want to reconnect. 98 99 ## Usage with other HTTP clients 100 101 You can also use any other HTTP client to establish a connection to the SSE endpoint. For example, you can use `curl`: 102 103 ```sh 104 $ curl -N "http://localhost:8080/events?channel=ChatChannel" 105 106 event: welcome 107 data: {"type":"welcome"} 108 109 event: confirm_subscription 110 data: {"type":"confirm_subscription","identifier":"{\"channel\":\"ChatChannel\"}"} 111 112 event: ping 113 data: {"type":"ping","message":1694041735} 114 115 data: {"message":"hello"} 116 ... 117 ``` 118 119 AnyCable also supports setting up a streaming HTTP connection via POST requests. In this case, you can provide a list of client-server commands in the request body using the JSONL (JSON lines) format. 120 121 <!-- TODO: fetch example --> 122 123 Note that you must process different event types yourself. See below for the format. 124 125 ## Action Cable over SSE format 126 127 The server-client communication format is designed as follows: 128 129 - The `data` field contains the message payload. **IMPORTANT**: for clients connecting via a GET request, the payload only contains the `message` part of the original Action Cable payload; clients connecting via POST requests receive the full payload (e.g., `{"identifier":, "message": {"foo":1}}`). 130 - The optional `event` field contains the message type (if any); for example, `welcome`, `confirm_subscription`, `ping` 131 - The optional `id` field contains the message ID if reliable streaming is enabled. The message ID has a form or `<offset>/<stream_id>/<epoch>` (see [Extended Action Cable protocol](/misc/action_cable_protocol.md#action-cable-extended-protocol)) 132 - The optional `retry` field contains the reconnection interval in milliseconds. We only set this field for `disconnect` messages with `reconnect: false` (it's set to a reasonably high number to prevent automatic reconnection attempts by EventSource). 133 134 Here is an example of a stream of messages from the server: 135 136 ```txt 137 event: welcome 138 data: {"type":"welcome"} 139 140 141 event: confirm_subscription 142 data: {"type":"confirm_subscription","identifier":"{\"channel\":\"ChatChannel\"}"} 143 144 145 event: ping 146 data: {"type":"ping","message":1694041735} 147 148 149 <!-- GET connection (e.g., EventSource) --> 150 data: {"message":"hello"} 151 id: 1/chat_42/y2023 152 153 data: {"message":"good-bye"} 154 id: 2/chat_42/y2023 155 156 157 <!-- POST connection --> 158 data: {"identifier":"{\"channel\":\"ChatChannel\"}","message":{"message":"hello"}} 159 id: 1/chat_42/y2023 160 161 162 data: {"identifier":"{\"channel\":\"ChatChannel\"}","message":{"message":"good-bye"}} 163 id: 2/chat_42/y2023 164 165 166 event: ping 167 data: {"type":"ping","message":1694044435} 168 169 170 event: disconnect 171 data: {"type":"disconnect","reason":"remote","reconnect":false} 172 retry: 31536000000 173 ```