# Signaling Server Documentation

The signaling server allows multiple clients to exchange messages with each other through various communication topics.

Topics are not defined upfront, but clients define them by subscribing to them. By subscribing to a given topic, the client tells the server to keep track of its unread messages in the given topic. By unsubscribing from a topic, the client tells the server to free the bookeeping it maintains for the given client and topic.

Every client communicates with the server via `GET` or `POST`. Clients must have a unique identifier, which can be randomly generated. This identifier should be included as a parameter named `subscriber_id` in every request.

Available operations:

- Subscribe to topics
- Unsubscribe from topics
- Publish a message
- Read pending messages
- Ping the server

## Subscribe to topics

To subscribe to a set of topics, a client must send a `POST` request with the following parameters:

- `subscriber_id`: Subscriber ID of the client.
- `action`: should be set to `gutenberg_signaling_server`.
- `message`: 
    - `type`: should be set to `subscribe`.
    - `topics`: array of topics that the client is interested in reading messages from, e.g., `[ 'WordPress', 'Drupal' ]`.

If the action is executed successfully by the server, the server will respond with `{"result":"ok"}`.

### Sample request

```js
await (
	await fetch( window.wp.ajax.settings.url, {
		body: new URLSearchParams( {
			subscriber_id: '1',
			action: 'gutenberg_signaling_server',
			message: JSON.stringify( {
				type: 'subscribe',
				topics: [ 'WordPress', 'Drupal' ],
			} ),
		} ),
		method: 'POST',
	} )
 ).text();
```

## Unsubscribe from topics

To unsubscribe from a set of topics, a client must send a `POST` request with the following parameters:

- `subscriber_id`: subscriber ID of the client.
- `action`: should be set to `gutenberg_signaling_server`.
- `message`: 
    - `type`: should be set as `unsubscribe`.
    - `topics`: an array of topics that the client is no longer interested in reading messages from, e.g., `['WordPress', 'Drupal']`.

If the action is executed successfully by the server, the server will respond with `{"result":"ok"}`.

### Sample request

```js
await (
	await fetch( window.wp.ajax.settings.url, {
		body: new URLSearchParams( {
			subscriber_id: '1',
			action: 'gutenberg_signaling_server',
			message: JSON.stringify( {
				type: 'unsubscribe',
				topics: [ 'WordPress', 'Drupal' ],
			} ),
		} ),
		method: 'POST',
	} )
 ).text();
```

## Publish a message

To publish a message in a specific topic, a client must send a `POST` request with the following parameters:

- `subscriber_id`: subscriber ID of the client.
- `action`: should be set to `gutenberg_signaling_server`.
- `message`:
    - `type`: should be set as `publish`.
    - `topic`: the topic where the message should be published, e.g., `WordPress`.
    - `data`: The data to be broadcasted to every client that subscribed to the topic. The data can be any string and may be encrypted to prevent the server from reading the messages.

If the action is executed successfully by the server, the server will respond with `{"result":"ok"}`.

### Sample request

```js
await (
	await fetch( window.wp.ajax.settings.url, {
		body: new URLSearchParams( {
			subscriber_id: '1',
			action: 'gutenberg_signaling_server',
			message: JSON.stringify( {
				type: 'publish',
				topic: 'WordPress',
				data: 'hello I am client 1!',
			} ),
		} ),
		method: 'POST',
	} )
 ).text();
```

## Read pending messages

To read pending messages, the client should send a `GET` request with the following parameters:

- `subscriber_id`: Subscriber ID of the client.
- `action`: should be set to `gutenberg_signaling_server`.

The server will respond using the [Event stream format](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format), whose content type is set to `text/event-stream;charset=UTF-8`. The Event stream format defines the following fields:

- `retry`: the reconnection time, in ms. The time after which the client should check again for messages.
- `id`: unique identifier for the server response.
- `event`: one of `message` or `error`.
- `data`:
	- If `event` is `message`, data is a JSON encoded string containing an array of messages that the given client has not read yet. Each message is similar to the published message object but includes an additional property named `clients`. This property specifies the number of clients for which the message was sent. Note it does not indicate whether they have already received/requested it.
	- If `event` is `error`, data is a description of the error.

If there are no pending messages, the server's response only contains the `retry:` field. If there are pending messages, the server will respond including all the fields.

### Sample request

```js
await (
	await fetch(
		window.wp.url.addQueryArgs( window.wp.ajax.settings.url, {
			subscriber_id: '1',
			action: 'gutenberg_signaling_server',
		} )
	)
 ).text();
```

Sample answer from the server when there are no unread messages:

```
retry: 3000
```

Sample answer from the server when there are unread messages:

```
retry: 3000
id: 1694809781
event: message
data: [{"type":"publish","topic":"WordPress","data":"hello I am client 1!","clients":2},{"type":"publish","topic":"WordPress","data":"Hi client 1 I am client 2","clients":2}]
```

## Ping the server

To ensure that the server is listening and to indicate that a client is still alive, the client can periodically send a ping to the server. When the server receives a ping from a client, it will respond with a message containing `pong`. The client will receive this `pong` message when it asks the server for new messages.

To send a ping, the client should send a `POST` request with the following parameters:

- `subscriber_id`: Subscriber ID of the client.
- `action`: should be set to `gutenberg_signaling_server`.
- `message`:
  - `type`: Should be set as `ping`.

If the action is executed successfully by the server, the server will respond with `{"result":"ok"}`.

#### Sample request

```js
await (
	await fetch( window.wp.ajax.settings.url, {
		body: new URLSearchParams( {
			subscriber_id: '1',
			action: 'gutenberg_signaling_server',
			message: JSON.stringify( {
				type: 'ping',
			} ),
		} ),
		method: 'POST',
	} )
 ).text();
```
