/WS-PushServer

(Dockerized) WebSocket Push-Server

Primary LanguageJavaMIT LicenseMIT

Header

WebSocket Push-Server

Version BuiltWith Build License

WebSocket Push-Server with channeling capabilities

It allows HTTP clients to create channels and send push messages to WebSocket clients that are subscribed to the same channel.

IntroductionSetupUsageDependenciesLicense


Introduction

Push architecture

A push architecture is used in applications in which the server has to send messages / data to the client without the client having to request it. This is particularly useful for applications that require real-time data, such as chat applications or stock market applications.

However, it is difficult to achieve this with HTTP for web applications as it is primarily a request-response protocol.

WebSockets

WebSocket connections are a solution to this problem. They allow a persistent connection between client and server, which can be used to send messages in both directions.

Push-Server

This project implements a Push-Server that uses WebSockets to create a push architecture that can be used for an unlimited amount of projects.

HTTP clients - let's call them publishers - can create channels where they can later send messages in. One channel should only be used for one project, so that the messages are only sent to the clients of this project.

WebSocket clients - also called subscribers - can subscribe (or unsubscribe) to (from) an unlimited amount of channels once they've established a WebSocket connection. They'll receive all messages that are sent to the channels they're subscribed to - and only those. Publishers can also address a message to a specific list of subscribers. Only those subscribers will receive the message - and only if they've subscribed to the channel.

For further information on how to use the Push-Server, see Usage.

Setup

This project is dockerized so you can easily run it in a container. It's recommended to use the pre-built image from the GitHub container registry - however you are also free to build the image yourself. Please follow the instructions below to get started.

  1. Clone the repository on the target environment
    git clone https://github.com/JensOstertag/WS-PushServer.git pushserver
  2. Configure the docker-compose.yml file
    • Change the ports if you want to use different ones from 5222 and 5223
    • Àdd a custom network mode (depending on your existing setup)
  3. Start the container with docker compose
    docker compose up -d

Usage

For the following examples, the server should be reachable on localhost, the WebSocket service should be available on port 5222 and the HTTP service should be available on port 5223. You can change these ports when starting the docker container, in which case you have to adjust the parameters that are used in the examples below.

Publishers

Publishers can create channels and send messages to the subscribers of these channels.

Create channels

You can create a channel by sending a POST request to the /channel/create route:

POST /channel/create HTTP/1.1
Host: localhost:5223

{
  "messageType": "SERVER_ACTION",
  "action": "CREATE_CHANNEL",
  "data": {
    "channel": "channelName"
  }
}
Field Description
data.channel The name of the channel that you want to create
Response - Success

You'll receive a JSON response when the channel was created successfully:

{
  "messageType": "SERVER_ACK",
  "code": 200,
  "message": "Created",
  "data": {
    "channel": "channelName",
    "channelToken": "CHANNEL_TOKEN"
  }
}
Field Description
data.channel The name of the channel that was created
data.channelToken The token that has to be used to send messages to the channel's subscribers

The channel token needs to be saved in a secure place. You'll need it every time you want to perform an action on the channel, such as sending a message to its subscribers.

Response - Error

If there was an error whilst creating the channel, you'll receive the following response:

{
  "messageType": "ERROR",
  "code": 409,
  "message": "Message",
  "data": {
    "errorDetails": "Details"
  }
}
Field Description
message Short description of what happened
data.errorDetails Details about the error that occurred
Ping channels

Channels can be pinged to check whether they exist or to get information about it.

POST /channel/ping HTTP/1.1
Host: localhost:5223

{
  "messageType": "SERVER_ACTION",
  "action": "PING_CHANNEL",
  "data": {
    "channel": "channelName",
    "channelToken": "CHANNEL_TOKEN"
  }
}
Field Description
data.channel The name of the channel that you want to ping
data.channelToken The channel token or null if you only want to know whether the channel exists
Response - Success

If the channel exists and the specified channel token is correct, you'll receive a response which looks like this:

{
  "messageType": "SERVER_ACK",
  "code": 200,
  "message": "OK",
  "data": {
    "channel": "channelName",
    "channelToken": null
  }
}
Field Description
data.channel The name of the channel that was pinged
data.channelToken Always null (it's only present to comply with the JSON schema)
Response - Error

If there was an error whilst pinging the channel, the response will look like this:

{
  "messageType": "ERROR",
  "code": 0,
  "message": "Message",
  "data": {
    "errorDetails": "Details"
  }
}
Field Description
message Short description of what happened
data.errorDetails Details about the error that occurred
Send messages

Messages can be sent to all subscribers of a channel or to a list of specific subscribers.

POST /push HTTP/1.1
Host: localhost:5223

{
  "messageType": "SERVER_ACTION",
  "action": "PUSH_MESSAGE",
  "data": {
    "channel": "channelName",
    "channelToken": "channelToken",
    "recipients": [],
    "message": "message"
  }
}
Field Description
data.channel The name of the channel that you want to send the message in
data.channelToken The channel token that you received when creating the channel
data.recipients A list of UUIDs of recipients that should receive the message
If this list is empty, the message will be sent to all subscribers of the channel
data.message The message that should be sent

The recipients can be specified in order to create a private messaging system. As this requires specification of the client's UUID, clients have to send an API call (or something comparable) to the backend of your application with their UUID upon subscribing to a channel.

Response - Success

In case of a successful message push, you'll receive the following response:

{
  "messageType": "SERVER_ACK",
  "code": 200,
  "message": "Sent",
  "data": {
    "channel": "channelName",
    "channelToken": null
  }
}
Field Description
data.channel The name of the channel in which the message was sent
data.channelToken Always null (it's only present to comply with the JSON schema)
Response - Error

If there was an error whilst sending the message, the response will look like this:

{
  "messageType": "ERROR",
  "code": 0,
  "message": "Message",
  "data": {
    "errorDetails": "Details"
  }
}
Field Description
message Short description of what happened
data.errorDetails Details about the error that occurred
Delete channels

It's a good practice to delete channels that are no longer in use. This can be done by sending a POST request to the /channel/delete route:

POST /channel/delete HTTP/1.1

{
  "messageType": "SERVER_ACTION",
  "action": "DELETE_CHANNEL",
  "data": {
    "channel": "channelName",
    "channelToken": "channelToken"
  }
}
Field Description
data.channel The name of the channel that you want to delete
data.channelToken The channel token that you received when creating the channel
Response - Success

If the channel was deleted successfully, you'll receive the following response:

{
  "messageType": "SERVER_ACK",
  "code": 200,
  "message": "Deleted",
  "data": {
    "channel": "channelName",
    "channelToken": null
  }
}
Field Description
data.channel The name of the channel that was deleted
data.channelToken Always null (it's only present to comply with the JSON schema)
Response - Error

If there was an error whilst deleting the channel, the response will look like this:

{
  "messageType": "ERROR",
  "code": 0,
  "message": "Message",
  "data": {
    "errorDetails": "Details"
  }
}
Field Description
message Short description of what happened
data.errorDetails Details about the error that occurred

Subscribers

Subscribers can subscribe to channels and receive messages that are sent to these channels.

Connection

To connect to the Push-Server, a client has to establish a WebSocket connection to ws://localhost:5222.

The client will automatically receive a message from the server:

{
  "messageType": "CLIENT_ACK",
  "code": 200,
  "message": "Connected",
  "data": {
    "uuid": "clientUuid",
    "subscribedChannels": []
  }
}
Field Description
data.uuid The UUID of the client that was generated by the server
It is used by publishers to send a message to specific clients
data.subscribedChannels A list of channels that the client is subscribed to
Initially empty
Subscribing to channels

Before a client can receive messages, he has to subscribe to a channel. This can be done by sending a message to the server:

{
  "messageType": "CLIENT_ACTION",
  "action": "SUBSCRIBE",
  "data": {
    "channel": "channelName"
  }
}
Field Description
data.channel The name of the channel that should be subscribed
Response - Success

If the channel was subscribed successfully, the client will receive the following response:

{
  "messageType": "CLIENT_ACK",
  "code": 200,
  "message": "Connected",
  "data": {
    "uuid": "clientUuid",
    "subscribedChannels": [
      "channelName"
    ]
  }
}
Field Description
data.uuid The UUID of the client that was generated by the server
data.subscribedChannels A list of channels that the client is subscribed to
The channel that was subscribed to is now in this list
Unsubscribing from channels

If a client wishes to no longer receive messages from a specific channel, he can unsubscribe from that channel by sending a message to the server:

{
  "messageType": "CLIENT_ACTION",
  "action": "UNSUBSCRIBE",
  "data": {
    "channel": "channelName"
  }
}
Field Description
data.channel The name of the channel that should be unsubscribed
Response - Success

If the client unsubscribed from the channel successfully, the client will receive the following response:

{
  "messageType": "CLIENT_ACK",
  "code": 200,
  "message": "Connected",
  "data": {
    "uuid": "clientUuid",
    "subscribedChannels": []
  }
}
Field Description
data.uuid The UUID of the client that was generated by the server
data.subscribedChannels A list of channels that the client is subscribed to
The channel that was unsubscribed is no longer in this list
Receiving messages

Messages are sent to the client in JSON format:

{
  "messageType": "CLIENT_PUSH",
  "code": 200,
  "message": "Push Message",
  "data": {
    "uuid": "clientUuid",
    "subscribedChannels": [
      "channelName"
    ],
    "pushMessage": {
      "channel": "channelName",
      "message": "Message"
    }
  }
}
Field Description
data.uuid The client's UUID
data.subscribedChannels The list of channels that the client is subscribed to
data.pushMessage.channel The name of the channel that the message was sent on
data.pushMessage.message The raw message that was sent on the channel

Dependencies

This project uses the following dependencies:

License

This project is licensed under the MIT License.