{"id":13505070,"url":"https://github.com/Kitura/Kitura-WebSocket","last_synced_at":"2025-03-29T22:31:26.374Z","repository":{"id":55631243,"uuid":"71998019","full_name":"Kitura/Kitura-WebSocket","owner":"Kitura","description":"WebSocket support for Kitura","archived":false,"fork":false,"pushed_at":"2020-12-16T19:31:19.000Z","size":871,"stargazers_count":66,"open_issues_count":9,"forks_count":28,"subscribers_count":25,"default_branch":"master","last_synced_at":"2024-05-21T06:07:15.749Z","etag":null,"topics":["server","swift","websocket"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kitura.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-26T11:50:21.000Z","updated_at":"2023-07-01T17:31:29.000Z","dependencies_parsed_at":"2022-08-15T05:00:25.841Z","dependency_job_id":null,"html_url":"https://github.com/Kitura/Kitura-WebSocket","commit_stats":null,"previous_names":["ibm-swift/kitura-websocket"],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kitura%2FKitura-WebSocket","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kitura%2FKitura-WebSocket/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kitura%2FKitura-WebSocket/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kitura%2FKitura-WebSocket/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kitura","download_url":"https://codeload.github.com/Kitura/Kitura-WebSocket/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246254077,"owners_count":20747946,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["server","swift","websocket"],"created_at":"2024-08-01T00:00:57.849Z","updated_at":"2025-03-29T22:31:24.563Z","avatar_url":"https://github.com/Kitura.png","language":"Swift","funding_links":[],"categories":["Monitoring"],"sub_categories":["WebSockets"],"readme":"\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"http://kitura.io/\"\u003e\n        \u003cimg src=\"https://raw.githubusercontent.com/Kitura/Kitura/master/Sources/Kitura/resources/kitura-bird.svg?sanitize=true\" height=\"100\" alt=\"Kitura\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://kitura.github.io/Kitura-WebSocket/index.html\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/apidoc-KituraWebSocket-1FBCE4.svg?style=flat\" alt=\"APIDoc\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://travis-ci.org/Kitura/Kitura-WebSocket\"\u003e\n    \u003cimg src=\"https://travis-ci.org/Kitura/Kitura-WebSocket.svg?branch=master\" alt=\"Build Status - Master\"\u003e\n    \u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/badge/os-macOS-green.svg?style=flat\" alt=\"macOS\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/os-linux-green.svg?style=flat\" alt=\"Linux\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-Apache2-blue.svg?style=flat\" alt=\"Apache 2\"\u003e\n    \u003ca href=\"http://swift-at-ibm-slack.mybluemix.net/\"\u003e\n    \u003cimg src=\"http://swift-at-ibm-slack.mybluemix.net/badge.svg\" alt=\"Slack Status\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n# Kitura-WebSocket\n\nKitura-WebSocket provides Kitura based servers the ability to receive and send messages to clients using the WebSocket\nprotocol (RFC 6455). It is compatible with a variety of WebSocket clients, including:\n- The built-in WebSocket support in the Chrome, FireFox, and Safari browsers.\n- The NPM [websocket](https://www.npmjs.com/package/websocket) package.\n\nKitura-WebSocket supports version thirteen of the WebSocket protocol.\n\nBoth the WS and WSS (SSL/TLS secured WS) protocols are supported by Kitura-WebSocket.\nTo enable WSS set up your Kitura based server for SSL/TLS support. See the tutorial\n[Enabling SSL/TLS on your Kitura server](https://www.kitura.io/guides/building/ssl.html) on\n[www.kitura.io](http://www.kitura.io) for details.\n\n## Table of Contents\n* [Usage](#usage)\n* [API Overview](#api-overview)\n* [Autobahn TestSuite](#Autobahn-TestSuite)\n* [Example - Simple](#example-simple)\n* [Example - Advanced](#example-advanced)\n* [API Documentation](#api-documentation)\n* [Community](#community)\n* [License](#license)\n\n## Usage\n\n#### Add dependencies\n\nAdd the `Kitura-WebSocket` package to the dependencies within your application’s `Package.swift` file. Substitute `\"x.x.x\"` with the latest `Kitura-WebSocket` [release](https://github.com/Kitura/Kitura-WebSocket/releases).\n\n```swift\n.package(url: \"https://github.com/Kitura/Kitura-WebSocket.git\", from: \"x.x.x\")\n```\n\nAdd `Kitura-WebSocket` to your target's dependencies:\n\n```swift\n.target(name: \"example\", dependencies: [\"Kitura-WebSocket\"]),\n```\n\n#### Import package\n\n  ```swift\n  import KituraWebSocket\n  ```\n\n## API Overview\nThe following is an overview of the Kitura-WebSocket APIs, for more information see the [API reference](https://kitura.github.io/Kitura-WebSocket/index.html).\n\nWhen using the WebSocket protocol, clients connect to WebSocket services running on a particular server. WebSocket services are identified on a particular server via a path. This path is sent in the upgrade request used to upgrade a connection from HTTP 1.1 to WebSocket.\n\nThe Kitura-WebSocket API reflects that interaction using the class `WebSocketConnection` which represents a WebSocket client's connection to a service and the protocol `WebSocketService` which is implemented by classes that are WebSocket services.\n\nA specific `WebSocketConnection` object is connected to a specific `WebSocketService` instance. On the other hand a specific `WebSocketService` instance is connected to many `WebSocketConnection` objects.\n\n### WebSocketConnection\nThe WebSocketConnection class provides:\n- Functions to send text and binary messages to the client.\n\n```swift\nWebSocketConnection.send(message: Data)\nWebSocketConnection.send(message: String)\n```\n- Functions to close the connection gracefully and forcefully.\n\n```swift\nWebSocketConnection.close(reason: WebSocketCloseReasonCode?=nil, description: String?=nil)\nWebSocketConnection.drop(reason: WebSocketCloseReasonCode?=nil, description: String?=nil)\n```\n\nIn both `close()` and `drop()`, the `WebSocketCloseReasonCode` enum provides the standard WebSocket close reason codes with the ability to specify application specific ones.\n\n- A unique identifier that can be used to help manage the collection of `WebSocketConnection` objects connected to a `WebSocketService`.\n\n```swift\nid: String\n```\n\n### WebSocketService\nThe WebSocketService protocol enables Kitura-WebSocket to notify a WebSocket service about a set of events that\noccur. These events include:\n- A client has connected to the WebSocketService.\n\n```swift\nconnected(connection: WebSocketConnection)\n```\n\n- A client has disconnected from the WebSocketService.\n\n```swift\ndisconnected(connection: WebSocketConnection, reason: WebSocketCloseReasonCode)\n```\nThe reason parameter contains the reason code associated with the client disconnecting. It may come from either\na close command sent by the client, or be determined by Kitura-WebSocket if the connection's socket suddenly was closed.\n\n- A binary message was received from a client.\n\n```swift\nreceived(message: Data, from: WebSocketConnection)\n```\nThe message parameter contains the bytes of the message in the form of a Data struct.\n\n- A text message was received from a client.\n\n```swift\nreceived(message: String, from: WebSocketConnection)\n```\nThe message parameter contains the message in the form of a String.\n\n#### Detect and close broken connections\n\nWebSocketService protocol has an optional Int property called `connectionTimeout`. This is the time in seconds that a connection must be unresponsive to be automatically closed by the server. If the WebSocket server has not received any messages in the first half of the timeout time it will ping the connection. If a pong is not received in the remaining half of the timeout, the connection will be closed with a 1006 (connection closed abnormally) status code. The `connectionTimeout` defaults to `nil`, meaning no connection cleanup will take place.  \n\nYou can set the `connectionTimeout` by assigning a value to the property within your WebSocketService class:\n\n```swift\nlet connectionTimeout: Int? = 60\n```\n\n### WebSocket\n\nClasses which implement the `WebSocketService` protocol are registered with the server as follows:\n\n```swift\nWebSocket.register(service: WebSocketService, onPath: String)\n```\nThis function is passed the `WebSocketService` being registered along with the path it is being registered on.\n\nA registered `WebSocketService` can be unregistered from the server as follows:\n\n```swift\nWebSocket.unregister(path: String)\n```\nThis function is passed the path which the `WebSocketService` being unregistered was registered on.\n\n## Autobahn TestSuite\nKitura-WebSocket complies to the [Autobahn Testsuite](https://github.com/crossbario/autobahn-testsuite) for web sockets. To create an echo server and run this test suite against it, follow the instructions [here](https://github.com/Kitura/Kitura-WebSocket/blob/master/AutobahnTests.md).\n\n## Example - Simple\nThis example is a simplistic chat service to demonstrate how to use the Kitura-WebSocket APIs.\nThe server side is written in Swift using Kitura-WebSocket and the client side is written in JavaScript using\nNode.js and the websocket NPM package. The instructions below show you how to create the files for both the server and client and then how to compile and run the application.\n\n### Pre-requisites\nIn order to run the client one must have Node.js installed.\n\n### The server\nThe server keeps track of the clients that have connected to it and echoes all text messages sent to it to all\nof the clients that have connected to it, with the exception of the client that sent the message.\n\nYou will need to create the server's directory structure to be something like this:\n\u003cpre\u003e\nServerDirectory\n├── Package.swift\n└── Sources\n    └── ChatServer\n        ├── ChatService.swift\n        └── main.swift\n\u003c/pre\u003e\n\nCreate a `Package.swift` file with the following content, substituting `\"x.x.x\"` with the latest releases of Kitura, HeliumLogger and Kitura-WebSocket:\n\n```swift\n// swift-tools-version:4.0\nimport PackageDescription\n\nlet package = Package(\n    name: \"ChatServer\",\n    dependencies: [\n         .package(url: \"https://github.com/Kitura/Kitura.git\", .upToNextMinor(from: \"x.x.x\")),\n         .package(url: \"https://github.com/Kitura/HeliumLogger.git\", from: \"x.x.x\"),\n         .package(url: \"https://github.com/Kitura/Kitura-WebSocket.git\", from: \"x.x.x\")\n    ],\n    targets: [\n        .target(\n            name: \"ChatServer\",\n            dependencies: [\"Kitura\", \"HeliumLogger\", \"Kitura-WebSocket\"]),\n    ]\n)\n```\nThe HeliumLogger package, while strictly not required, was added to enable logging.\n\nCreate a `ChatService.swift` file which contains:\n```swift\n// ChatServer is a very simple chat server\n\nimport Foundation\n\nimport KituraWebSocket\n\nclass ChatService: WebSocketService {\n\n    private var connections = [String: WebSocketConnection]()\n    \n    let connectionTimeout: Int? = 60\n\n    public func connected(connection: WebSocketConnection) {\n        connections[connection.id] = connection\n    }\n\n    public func disconnected(connection: WebSocketConnection, reason: WebSocketCloseReasonCode) {\n        connections.removeValue(forKey: connection.id)\n    }\n\n    public func received(message: Data, from: WebSocketConnection) {\n        from.close(reason: .invalidDataType, description: \"Chat-Server only accepts text messages\")\n\n        connections.removeValue(forKey: from.id)\n    }\n\n    public func received(message: String, from: WebSocketConnection) {\n        for (connectionId, connection) in connections {\n            if connectionId != from.id {\n                connection.send(message: message)\n            }\n        }\n    }\n}\n```\nThe class has a Dictionary, connections, which is used to keep track of the connections of all of the connected clients. The Dictionary is\nmaintained by the `connected` and `disconnected` functions, which are, respectively, adding and removing connections from the dictionary.\n\nThe `received` function, which receives binary messages, is rejecting the message, closing the client connection and removing the connection\nfrom the set of known connections.\n\nLastly, the `received` function, which receives text messages, simply echoes the message received to all clients except the one who sent the message.\n\nIt should be noted that all of these functions can be invoked from many threads simultaneously. In real applications,\none should add locking around the access of non-thread safe artifacts of the application such as the\nconnections Dictionary in this very simplistic example.\n\nCreate a `main.swift` file which contains:\n```swift\n// ChatServer is a very simple chat server\n\nimport Foundation\n\nimport KituraNet\nimport KituraWebSocket\n\nimport HeliumLogger\nimport LoggerAPI\n\n// Using an implementation for a Logger\nHeliumLogger.use(.info)\n\nWebSocket.register(service: ChatService(), onPath: \"chat\")\n\nclass ChatServerDelegate: ServerDelegate {\n    public func handle(request: ServerRequest, response: ServerResponse) {}\n}\n\n// Add HTTP Server to listen on port 8080\nlet server = HTTP.createServer()\nserver.delegate = ChatServerDelegate()\n\ndo {\n    try server.listen(on: 8080)\n    ListenerGroup.waitForListeners()\n} catch {\n    Log.error(\"Error listening on port 8080: \\(error).\")\n}\n```\nIn the main.swift file:\n- The HeliumLogger is set up to log info, warning, and error type messages.\n- The ChatService defined in the ChatService.swift file is registered on the path *chat*.\n- An HTTP server is created and setup to listen on port 8080.\n\nWith this server set up clients should connect to the chat service as *ws://__host__:8080/chat*, where **host** is the host running the server.\n\n### The client\nThe client has a simple command line interface. At startup one passes the host and port number. The client simply reads\nmessages to be sent from the terminal and displays messages received on the terminal as well.\n\nYou will need to create the client's directory structure to be something like this:\n\u003cpre\u003e\nClientDirectory\n├── package.json\n└── chat.js\n\u003c/pre\u003e\n\nCreate a `package.json` file which, at a minimum, contains:\n\n```javascript\n{\n  \"name\": \"chat\",\n  \"description\": \"Simple chat server client\",\n  \"version\": \"0.0.1\",\n  \"engines\": {\n    \"node\": \"\u003e=0.8.0\"\n  },\n  \"dependencies\": {\n    \"websocket\": \"^1.0.23\"\n  }\n}\n```\n\nCreate a `chat.js` file which contains:\n\n```javascript\n/* main file of Simple Chat Server Client */\n\nvar readline = require('readline');\nvar WebSocketClient = require('websocket').client\n\nvar host = process.argv[2];\n\nrl = readline.createInterface(process.stdin, process.stdout);\n\nrl.setPrompt('\u003e ');\nrl.prompt();\nvar client = new WebSocketClient();\n\nclient.on('connectFailed', function(error) {\n    console.log('Connect Error: ' + error.toString());\n    process.exit();\n});\n\nclient.on('connect', function(connection) {\n    connection.on('error', function(error) {\n        console.log(\"Connection Error: \" + error.toString());\n        process.exit();\n    });\n\n    connection.on('close', function(reasonCode, description) {\n        console.log('chat Connection Closed. Code=' + reasonCode + ' (' + description +')');\n    });\n\n    connection.on('message', function(message) {\n        if (message.type === 'utf8') {\n            console.log('\\r=\u003e ' + message.utf8Data);\n            rl.prompt();\n        }   \n    });\n\n    rl.on('line', function(line) {\n        connection.sendUTF(line);\n        rl.prompt();\n    });\n\n    rl.on('close', function() {\n        connection.close();\n        console.log('Have a great day!');\n        process.exit(0);\n    });\n\n    rl.prompt();\n});\n\nclient.connect(\"ws://\" + host +\"/chat\", \"chat\");\n```\n\n### Building and running the example\nTo build the server, in the server directory, type:\n```\nswift build\n```\nTo run the server, in the same directory, type:\n```\n.build/debug/ChatServer\n```\nThe server will now be up and running. The informational log message below will be displayed:\n\n``\n[INFO] [HTTPServer.swift:124 listen(on:)] Listening on port 8080\n``\n\n### Setting up and running the client\nTo setup the client, in the client directory, simply:\n```\nnpm install\n```\nThat will install the websocket package.\n\nTo run the client, again in the client directory, run:\n```\nnode chat.js host:8080\n```\nWhere **host** is the hostname of the host on which the server is running, e.g. if your server is running on the localhost run:\n```\nnode chat.js localhost:8080\n```\n\nAs described above, the server echoes all text messages sent to it to all of the clients that have connected to it, with the exception of the client that sent the message. Therefore, in order to see the example in action you will need to connect more than one client to the server. The client can be run in several terminal windows on the same computer. You can then enter a message on one client and see it appear on another client and vice versa.\n\n## Example - Advanced\nFor a more complete example please see [Kitura-Sample](https://github.com/Kitura/Kitura-Sample).\n\n## API Documentation\nFor more information visit our [API reference](https://kitura.github.io/Kitura-WebSocket/index.html).\n\n## Community\n\nWe love to talk server-side Swift, and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team!\n\n## License\nThis library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/Kitura/Kitura-WebSocket/blob/master/LICENSE.txt).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKitura%2FKitura-WebSocket","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FKitura%2FKitura-WebSocket","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKitura%2FKitura-WebSocket/lists"}