{"id":16573953,"url":"https://github.com/jensostertag/ws-pushserver","last_synced_at":"2026-04-13T08:31:26.653Z","repository":{"id":204052026,"uuid":"699696607","full_name":"JensOstertag/WS-PushServer","owner":"JensOstertag","description":"(Dockerized) WebSocket Push-Server","archived":false,"fork":false,"pushed_at":"2025-04-24T21:10:09.000Z","size":221,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-05T12:52:33.909Z","etag":null,"topics":["docker","java","push-server","websocket"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JensOstertag.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-10-03T06:46:35.000Z","updated_at":"2025-04-24T21:10:12.000Z","dependencies_parsed_at":"2023-11-09T10:36:30.319Z","dependency_job_id":"f781ec75-c86a-4ef4-ac3c-c39e6b50953b","html_url":"https://github.com/JensOstertag/WS-PushServer","commit_stats":null,"previous_names":["jensostertag/ws-pushserver"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/JensOstertag/WS-PushServer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JensOstertag%2FWS-PushServer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JensOstertag%2FWS-PushServer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JensOstertag%2FWS-PushServer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JensOstertag%2FWS-PushServer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JensOstertag","download_url":"https://codeload.github.com/JensOstertag/WS-PushServer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JensOstertag%2FWS-PushServer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31746101,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T06:26:45.479Z","status":"ssl_error","status_checked_at":"2026-04-13T06:26:44.645Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","java","push-server","websocket"],"created_at":"2024-10-11T21:43:28.935Z","updated_at":"2026-04-13T08:31:26.638Z","avatar_url":"https://github.com/JensOstertag.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n![Header](img/header.jpg)\n\n# WebSocket Push-Server\n\n![Version](https://img.shields.io/badge/version-v1.0.0-yellow?style=for-the-badge)\n![BuiltWith](https://img.shields.io/badge/built%20with-Java-orange?style=for-the-badge)\n![Build](https://img.shields.io/github/actions/workflow/status/JensOstertag/WS-PushServer/deploy-image.yml?style=for-the-badge)\n![License](https://img.shields.io/badge/license-MIT-blue?style=for-the-badge)\n\n\n\n### WebSocket Push-Server with channeling capabilities\n\nIt allows HTTP clients to create channels and send push messages to WebSocket clients that are subscribed to the same channel.\n\n[Introduction](#introduction) • [Setup](#setup) • [Usage](#usage) • [Dependencies](#dependencies) • [License](#license)\n\n\u003c/div\u003e\n\n\u003chr\u003e\n\n\u003ch2 id=\"introduction\"\u003eIntroduction\u003c/h2\u003e\n\n### Push architecture\nA 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.\nThis is particularly useful for applications that require real-time data, such as chat applications or stock market applications.\n\nHowever, it is difficult to achieve this with HTTP for web applications as it is primarily a request-response protocol.\n\n### WebSockets\nWebSocket connections are a solution to this problem.\nThey allow a persistent connection between client and server, which can be used to send messages in both directions.\n\n### Push-Server\nThis project implements a Push-Server that uses WebSockets to create a push architecture that can be used for an unlimited amount of projects.\n\nHTTP clients - let's call them publishers - can create channels where they can later send messages in.\nOne channel should only be used for one project, so that the messages are only sent to the clients of this project.\n\nWebSocket clients - also called subscribers - can subscribe (or unsubscribe) to (from) an unlimited amount of channels once they've established a WebSocket connection.\nThey'll receive all messages that are sent to the channels they're subscribed to - and only those.\nPublishers can also address a message to a specific list of subscribers.\nOnly those subscribers will receive the message - and only if they've subscribed to the channel.\n\nFor further information on how to use the Push-Server, see [Usage](#usage).\n\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\nThis project is dockerized so you can easily run it in a container.\nIt's recommended to use the pre-built image from the [GitHub container registry](https://ghcr.io/jensostertag/pushserver) - however you are also free to build the image yourself.\nPlease follow the instructions below to get started.\n\n1. Clone the repository on the target environment\n   ```sh\n   git clone https://github.com/JensOstertag/WS-PushServer.git pushserver\n   ```\n2. Configure the `docker-compose.yml` file\n   - Change the ports if you want to use different ones from `5222` and `5223`\n   - Àdd a custom network mode (depending on your existing setup)\n3. Start the container with docker compose\n   ```sh\n   docker compose up -d\n   ```\n\n\u003ch2 id=\"usage\"\u003eUsage\u003c/h2\u003e\n\nFor 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`.\nYou 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.\n\n### Publishers\nPublishers can create channels and send messages to the subscribers of these channels.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eCreate channels\u003c/strong\u003e\u003c/summary\u003e\n\nYou can create a channel by sending a `POST` request to the `/channel/create` route:\n```http\nPOST /channel/create HTTP/1.1\nHost: localhost:5223\n\n{\n  \"messageType\": \"SERVER_ACTION\",\n  \"action\": \"CREATE_CHANNEL\",\n  \"data\": {\n    \"channel\": \"channelName\"\n  }\n}\n```\n\n| Field          | Description                                     |\n|----------------|-------------------------------------------------|\n| `data.channel` | The name of the channel that you want to create |\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Success\u003c/summary\u003e\n\nYou'll receive a JSON response when the channel was created successfully:\n```json\n{\n  \"messageType\": \"SERVER_ACK\",\n  \"code\": 200,\n  \"message\": \"Created\",\n  \"data\": {\n    \"channel\": \"channelName\",\n    \"channelToken\": \"CHANNEL_TOKEN\"\n  }\n}\n```\n\n| Field               | Description                                                                 |\n|---------------------|-----------------------------------------------------------------------------|\n| `data.channel`      | The name of the channel that was created                                    |\n| `data.channelToken` | The token that has to be used to send messages to the channel's subscribers |\n\nThe channel token needs to be saved in a secure place.\nYou'll need it every time you want to perform an action on the channel, such as sending a message to its subscribers.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Error\u003c/summary\u003e\n\nIf there was an error whilst creating the channel, you'll receive the following response:\n```json\n{\n  \"messageType\": \"ERROR\",\n  \"code\": 409,\n  \"message\": \"Message\",\n  \"data\": {\n    \"errorDetails\": \"Details\"\n  }\n}\n```\n\n| Field               | Description                           |\n|---------------------|---------------------------------------|\n| `message`           | Short description of what happened    |\n| `data.errorDetails` | Details about the error that occurred |\n\u003c/details\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003ePing channels\u003c/strong\u003e\u003c/summary\u003e\n\nChannels can be pinged to check whether they exist or to get information about it.\n\n```http\nPOST /channel/ping HTTP/1.1\nHost: localhost:5223\n\n{\n  \"messageType\": \"SERVER_ACTION\",\n  \"action\": \"PING_CHANNEL\",\n  \"data\": {\n    \"channel\": \"channelName\",\n    \"channelToken\": \"CHANNEL_TOKEN\"\n  }\n}\n```\n\n| Field               | Description                                                                     |\n|---------------------|---------------------------------------------------------------------------------|\n| `data.channel`      | The name of the channel that you want to ping                                   |\n| `data.channelToken` | The channel token or `null` if you only want to know whether the channel exists |\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Success\u003c/summary\u003e\n\nIf the channel exists and the specified channel token is correct, you'll receive a response which looks like this:\n```json\n{\n  \"messageType\": \"SERVER_ACK\",\n  \"code\": 200,\n  \"message\": \"OK\",\n  \"data\": {\n    \"channel\": \"channelName\",\n    \"channelToken\": null\n  }\n}\n```\n\n| Field               | Description                                                      |\n|---------------------|------------------------------------------------------------------|\n| `data.channel`      | The name of the channel that was pinged                          |\n| `data.channelToken` | Always `null` (it's only present to comply with the JSON schema) |\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Error\u003c/summary\u003e\n\nIf there was an error whilst pinging the channel, the response will look like this:\n```json\n{\n  \"messageType\": \"ERROR\",\n  \"code\": 0,\n  \"message\": \"Message\",\n  \"data\": {\n    \"errorDetails\": \"Details\"\n  }\n}\n```\n\n| Field               | Description                           |\n|---------------------|---------------------------------------|\n| `message`           | Short description of what happened    |\n| `data.errorDetails` | Details about the error that occurred |\n\u003c/details\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eSend messages\u003c/strong\u003e\u003c/summary\u003e\n\nMessages can be sent to all subscribers of a channel or to a list of specific subscribers.\n    \n```http\nPOST /push HTTP/1.1\nHost: localhost:5223\n\n{\n  \"messageType\": \"SERVER_ACTION\",\n  \"action\": \"PUSH_MESSAGE\",\n  \"data\": {\n    \"channel\": \"channelName\",\n    \"channelToken\": \"channelToken\",\n    \"recipients\": [],\n    \"message\": \"message\"\n  }\n}\n```\n\n| Field               | Description                                                                                                                                          |\n|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `data.channel`      | The name of the channel that you want to send the message in                                                                                         |\n| `data.channelToken` | The channel token that you received when creating the channel                                                                                        |\n| `data.recipients`   | A list of UUIDs of recipients that should receive the message \u003cbr\u003e If this list is empty, the message will be sent to all subscribers of the channel |\n| `data.message`      | The message that should be sent                                                                                                                      |\n\nThe recipients can be specified in order to create a private messaging system.\nAs 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.\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Success\u003c/summary\u003e\n\nIn case of a successful message push, you'll receive the following response:\n```json\n{\n  \"messageType\": \"SERVER_ACK\",\n  \"code\": 200,\n  \"message\": \"Sent\",\n  \"data\": {\n    \"channel\": \"channelName\",\n    \"channelToken\": null\n  }\n}\n```\n\n| Field               | Description                                                      |\n|---------------------|------------------------------------------------------------------|\n| `data.channel`      | The name of the channel in which the message was sent            |\n| `data.channelToken` | Always `null` (it's only present to comply with the JSON schema) |\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Error\u003c/summary\u003e\n\nIf there was an error whilst sending the message, the response will look like this:\n```json\n{\n  \"messageType\": \"ERROR\",\n  \"code\": 0,\n  \"message\": \"Message\",\n  \"data\": {\n    \"errorDetails\": \"Details\"\n  }\n}\n```\n\n| Field               | Description                           |\n|---------------------|---------------------------------------|\n| `message`           | Short description of what happened    |\n| `data.errorDetails` | Details about the error that occurred |\n\u003c/details\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eDelete channels\u003c/strong\u003e\u003c/summary\u003e\n\nIt's a good practice to delete channels that are no longer in use.\nThis can be done by sending a `POST` request to the `/channel/delete` route:\n```http\nPOST /channel/delete HTTP/1.1\n\n{\n  \"messageType\": \"SERVER_ACTION\",\n  \"action\": \"DELETE_CHANNEL\",\n  \"data\": {\n    \"channel\": \"channelName\",\n    \"channelToken\": \"channelToken\"\n  }\n}\n```\n\n| Field               | Description                                                   |\n|---------------------|---------------------------------------------------------------|\n| `data.channel`      | The name of the channel that you want to delete               |\n| `data.channelToken` | The channel token that you received when creating the channel |\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Success\u003c/summary\u003e\n\nIf the channel was deleted successfully, you'll receive the following response:\n```json\n{\n  \"messageType\": \"SERVER_ACK\",\n  \"code\": 200,\n  \"message\": \"Deleted\",\n  \"data\": {\n    \"channel\": \"channelName\",\n    \"channelToken\": null\n  }\n}\n```\n\n| Field               | Description                                                      |\n|---------------------|------------------------------------------------------------------|\n| `data.channel`      | The name of the channel that was deleted                         |\n| `data.channelToken` | Always `null` (it's only present to comply with the JSON schema) |\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Error\u003c/summary\u003e\n\nIf there was an error whilst deleting the channel, the response will look like this:\n```json\n{\n  \"messageType\": \"ERROR\",\n  \"code\": 0,\n  \"message\": \"Message\",\n  \"data\": {\n    \"errorDetails\": \"Details\"\n  }\n}\n```\n\n| Field               | Description                           |\n|---------------------|---------------------------------------|\n| `message`           | Short description of what happened    |\n| `data.errorDetails` | Details about the error that occurred |\n\u003c/details\u003e\n\u003c/details\u003e\n\n### Subscribers\nSubscribers can subscribe to channels and receive messages that are sent to these channels.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eConnection\u003c/strong\u003e\u003c/summary\u003e\n\nTo connect to the Push-Server, a client has to establish a WebSocket connection to `ws://localhost:5222`.\n\nThe client will automatically receive a message from the server:\n```json\n{\n  \"messageType\": \"CLIENT_ACK\",\n  \"code\": 200,\n  \"message\": \"Connected\",\n  \"data\": {\n    \"uuid\": \"clientUuid\",\n    \"subscribedChannels\": []\n  }\n}\n```\n\n| Field                     | Description                                                                                                                 |\n|---------------------------|-----------------------------------------------------------------------------------------------------------------------------|\n| `data.uuid`               | The UUID of the client that was generated by the server \u003cbr\u003e It is used by publishers to send a message to specific clients |\n| `data.subscribedChannels` | A list of channels that the client is subscribed to \u003cbr\u003e Initially empty                                                    |\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eSubscribing to channels\u003c/strong\u003e\u003c/summary\u003e\n\nBefore a client can receive messages, he has to subscribe to a channel.\nThis can be done by sending a message to the server:\n```json\n{\n  \"messageType\": \"CLIENT_ACTION\",\n  \"uuid\": \"clientUuid\",\n  \"action\": \"SUBSCRIBE\",\n  \"data\": {\n    \"channel\": \"channelName\"\n  }\n}\n```\n\n| Field          | Description                                             |\n|----------------|---------------------------------------------------------|\n| `uuid`         | The UUID of the client that was generated by the server |\n| `data.channel` | The name of the channel that should be subscribed       |\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Success\u003c/summary\u003e\n\nIf the channel was subscribed successfully, the client will receive the following response:\n```json\n{\n  \"messageType\": \"CLIENT_ACK\",\n  \"code\": 200,\n  \"message\": \"Connected\",\n  \"data\": {\n    \"uuid\": \"clientUuid\",\n    \"subscribedChannels\": [\n      \"channelName\"\n    ]\n  }\n}\n```\n\n| Field                     | Description                                                                                                     |\n|---------------------------|-----------------------------------------------------------------------------------------------------------------|\n| `data.uuid`               | The UUID of the client that was generated by the server                                                         |\n| `data.subscribedChannels` | A list of channels that the client is subscribed to \u003cbr\u003e The channel that was subscribed to is now in this list |\n\u003c/details\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eUnsubscribing from channels\u003c/strong\u003e\u003c/summary\u003e\n\nIf 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:\n```json\n{\n  \"messageType\": \"CLIENT_ACTION\",\n  \"uuid\": \"clientUuid\",\n  \"action\": \"UNSUBSCRIBE\",\n  \"data\": {\n    \"channel\": \"channelName\"\n  }\n}\n```\n\n| Field          | Description                                             |\n|----------------|---------------------------------------------------------|\n| `uuid`         | The UUID of the client that was generated by the server |\n| `data.channel` | The name of the channel that should be unsubscribed     |\n\n\u003cdetails\u003e\n\u003csummary\u003eResponse - Success\u003c/summary\u003e\n\nIf the client unsubscribed from the channel successfully, the client will receive the following response:\n```json\n{\n  \"messageType\": \"CLIENT_ACK\",\n  \"code\": 200,\n  \"message\": \"Connected\",\n  \"data\": {\n    \"uuid\": \"clientUuid\",\n    \"subscribedChannels\": []\n  }\n}\n```\n\n| Field                     | Description                                                                                                          |\n|---------------------------|----------------------------------------------------------------------------------------------------------------------|\n| `data.uuid`               | The UUID of the client that was generated by the server                                                              |\n| `data.subscribedChannels` | A list of channels that the client is subscribed to \u003cbr\u003e The channel that was unsubscribed is no longer in this list |\n\u003c/details\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eReceiving messages\u003c/strong\u003e\u003c/summary\u003e\n\nMessages are sent to the client in JSON format:\n```json\n{\n  \"messageType\": \"CLIENT_PUSH\",\n  \"code\": 200,\n  \"message\": \"Push Message\",\n  \"data\": {\n    \"uuid\": \"clientUuid\",\n    \"subscribedChannels\": [\n      \"channelName\"\n    ],\n    \"pushMessage\": {\n      \"channel\": \"channelName\",\n      \"message\": \"Message\"\n    }\n  }\n}\n```\n\n| Field                      | Description                                           |\n|----------------------------|-------------------------------------------------------|\n| `data.uuid`                | The client's UUID                                     |\n| `data.subscribedChannels`  | The list of channels that the client is subscribed to |\n| `data.pushMessage.channel` | The name of the channel that the message was sent on  |\n| `data.pushMessage.message` | The raw message that was sent on the channel          |\n\u003c/details\u003e\n\n\u003ch2 id=\"dependencies\"\u003eDependencies\u003c/h2\u003e\nThis project uses the following dependencies:\n\n- Java\n  - **Java-WebSocket** v1.5.4 - GitHub: [TooTallNate/Java-WebSocket](https://github.com/TooTallNate/Java-WebSocket), licensed under [MIT License](https://github.com/TooTallNate/Java-WebSocket/blob/master/LICENSE)\n  - **Gson** v2.10.1 - GitHub: [google/gson](https://github.com/google/gson), licensed under [Apache License 2.0](https://github.com/google/gson/blob/main/LICENSE)\n  - **Json-Schema-Validator** v1.0.87 - GitHub: [networknt/json-schema-validator](https://github.com/networknt/json-schema-validator), licensed under [Apache License 2.0](https://github.com/networknt/json-schema-validator/blob/master/LICENSE)\n  - **TestNG** - GitHub: [cbeust/testng](https://github.com/testng-team/testng), licensed under [Apache License 2.0](https://github.com/testng-team/testng/blob/master/LICENSE.txt)\n  - **Maven** - GitHub: [apache/maven](https://github.com/apache/maven), licensed under [Apache License 2.0](https://github.com/apache/maven/blob/master/LICENSE)\n- Docker\n  - **maven** 3.8.7-openjdk-21-slim - DockerHub: [maven](https://hub.docker.com/_/maven), licensed under [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) \n  - **openjdk** 21-jdk-slim - DockerHub: [openjdk](https://hub.docker.com/_/openjdk), licensed under [GPLv2](https://openjdk.org/legal/gplv2+ce.html)\n- GitHub Actions\n  - **actions/checkout** v3 - GitHub: [actions/checkout](https://github.com/actions/checkout), licensed under [MIT License](https://github.com/actions/checkout/blob/main/LICENSE)\n  - **docker/login-action** v1 - GitHub: [docker/login-action](https://github.com/docker/login-action), licensed under [Apache License 2.0](https://github.com/docker/login-action/blob/master/LICENSE)\n\n\u003ch2 id=\"license\"\u003eLicense\u003c/h2\u003e\n\nThis project is licensed under the \u003ca href=\"./LICENSE\"\u003eMIT License\u003c/a\u003e.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjensostertag%2Fws-pushserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjensostertag%2Fws-pushserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjensostertag%2Fws-pushserver/lists"}