├── .gitignore ├── .jazzy.yaml ├── .swift-version ├── .travis.yml ├── AutobahnTests.md ├── LICENSE.txt ├── Package.swift ├── Package@swift-4.swift ├── README.md ├── Sources └── KituraWebSocket │ ├── WSConnectionUpgradeFactory.swift │ ├── WSFrame.swift │ ├── WSFrameParser.swift │ ├── WSServerRequest.swift │ ├── WSSocketProcessor.swift │ ├── WebSocket.swift │ ├── WebSocketCloseReasonCode.swift │ ├── WebSocketConnection.swift │ ├── WebSocketError.swift │ └── WebSocketService.swift ├── Tests ├── KituraWebSocketTests │ ├── BasicTests.swift │ ├── ComplexTests.swift │ ├── ConnectionCleanupTests.swift │ ├── KituraTest+Frames.swift │ ├── KituraTest.swift │ ├── PrintLogger.swift │ ├── ProtocolErrorTests.swift │ ├── TestLinuxSafeguard.swift │ ├── TestWebSocketService.swift │ └── UpgradeErrors.swift └── LinuxMain.swift └── docs ├── Classes.html ├── Classes ├── WSConnectionUpgradeFactory.html ├── WebSocket.html └── WebSocketConnection.html ├── Enums.html ├── Enums ├── WebSocketCloseReasonCode.html └── WebSocketError.html ├── Protocols.html ├── Protocols └── WebSocketService.html ├── badge.svg ├── css ├── highlight.css └── jazzy.css ├── docsets ├── KituraWebSocket.docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ ├── WSConnectionUpgradeFactory.html │ │ │ ├── WebSocket.html │ │ │ └── WebSocketConnection.html │ │ ├── Enums.html │ │ ├── Enums │ │ │ ├── WebSocketCloseReasonCode.html │ │ │ └── WebSocketError.html │ │ ├── Protocols.html │ │ ├── Protocols │ │ │ └── WebSocketService.html │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ ├── gh.png │ │ │ └── spinner.gif │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ ├── jazzy.search.js │ │ │ ├── jquery.min.js │ │ │ ├── lunr.min.js │ │ │ └── typeahead.jquery.js │ │ └── search.json │ │ └── docSet.dsidx └── KituraWebSocket.tgz ├── img ├── carat.png ├── dash.png ├── gh.png └── spinner.gif ├── index.html ├── js ├── jazzy.js ├── jazzy.search.js ├── jquery.min.js ├── lunr.min.js └── typeahead.jquery.js ├── search.json └── undocumented.json /.gitignore: -------------------------------------------------------------------------------- 1 | Package.resolved 2 | .build 3 | build 4 | .vagrant 5 | Packages* 6 | *.pkg 7 | Kitura-WebSocket.xcodeproj 8 | *.DS_Store 9 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | module: KituraWebSocket 2 | author: IBM & Kitura project authors 3 | github_url: https://github.com/Kitura/Kitura-WebSocket/ 4 | 5 | theme: fullwidth 6 | clean: true 7 | exclude: [Packages] 8 | 9 | readme: README.md 10 | 11 | skip_undocumented: false 12 | hide_documentation_coverage: false 13 | 14 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI build file. 2 | 3 | # whitelist (branches that should be built) 4 | branches: 5 | only: 6 | - master 7 | - /^issue.*$/ 8 | 9 | # the matrix of builds should cover each combination of Swift version 10 | # and platform that is supported. The version of Swift used is specified 11 | # by .swift-version, unless SWIFT_SNAPSHOT is specified. 12 | matrix: 13 | include: 14 | - os: linux 15 | dist: xenial 16 | sudo: required 17 | services: docker 18 | env: DOCKER_IMAGE=swift:4.0.3 SWIFT_SNAPSHOT=4.0.3 19 | - os: linux 20 | dist: xenial 21 | sudo: required 22 | services: docker 23 | env: DOCKER_IMAGE=swift:4.1.3 SWIFT_SNAPSHOT=4.1.3 24 | - os: linux 25 | dist: xenial 26 | sudo: required 27 | services: docker 28 | env: DOCKER_IMAGE=swift:4.2.4 SWIFT_SNAPSHOT=4.2.4 29 | - os: linux 30 | dist: xenial 31 | sudo: required 32 | services: docker 33 | env: DOCKER_IMAGE=swift:5.0.3-xenial SWIFT_SNAPSHOT=5.0.3 34 | - os: linux 35 | dist: xenial 36 | sudo: required 37 | services: docker 38 | env: DOCKER_IMAGE=swift:5.1 39 | - os: linux 40 | dist: xenial 41 | sudo: required 42 | services: docker 43 | env: DOCKER_IMAGE=swift:5.1 SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT 44 | - os: osx 45 | osx_image: xcode9.2 46 | sudo: required 47 | env: SWIFT_SNAPSHOT=4.0.3 48 | - os: osx 49 | osx_image: xcode9.4 50 | sudo: required 51 | env: SWIFT_SNAPSHOT=4.1.2 52 | - os: osx 53 | osx_image: xcode10.1 54 | sudo: required 55 | env: SWIFT_SNAPSHOT=4.2.1 56 | - os: osx 57 | osx_image: xcode10.2 58 | sudo: required 59 | env: SWIFT_SNAPSHOT=5.0.1 JAZZY_ELIGIBLE=true 60 | - os: osx 61 | osx_image: xcode11 62 | sudo: required 63 | - os: osx 64 | osx_image: xcode11 65 | sudo: required 66 | env: SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT 67 | 68 | before_install: 69 | - git clone https://github.com/Kitura/Package-Builder.git 70 | 71 | script: 72 | - ./Package-Builder/build-package.sh -projectDir $TRAVIS_BUILD_DIR 73 | -------------------------------------------------------------------------------- /AutobahnTests.md: -------------------------------------------------------------------------------- 1 | # WebSockets Autobahn-Testsuite 2 | 3 | This document will take you through the steps to test Kitura-Websockets using [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite). 4 | 5 | ### Creating a EchoServer 6 | These tests are run against a WebSocket EchoServer so we must first set one up. 7 | 8 | 1. Create a swift project: 9 | ``` 10 | mkdir ~/WebSocketEchoServer 11 | cd ~/WebSocketEchoServer 12 | swift package init --type executable 13 | open Package.swift 14 | ``` 15 | 16 | 2. Inside the `Package.swift` replace the text with: 17 | ```swift 18 | // swift-tools-version:4.0 19 | import PackageDescription 20 | 21 | let package = Package( 22 | name: "WebSocketEchoServer", 23 | dependencies: [ 24 | .package(url: "https://github.com/Kitura/Kitura.git", from: "2.3.0"), 25 | .package(url: "https://github.com/Kitura/HeliumLogger.git", .upToNextMinor(from: "1.7.0")), 26 | .package(url: "https://github.com/Kitura/Kitura-WebSocket.git", from: "2.0.0") 27 | ], 28 | targets: [ 29 | .target( 30 | name: "WebSocketEchoServer", 31 | dependencies: ["Kitura", "HeliumLogger", "Kitura-WebSocket"]), 32 | ] 33 | ) 34 | ``` 35 | 3. Open the projects main.swift file: 36 | ``` 37 | open ~/WebSocketEchoServer/Sources/WebSocketEchoServer/main.swift 38 | ``` 39 | 4. Inside the `main.swift` file replace `print("Hello, world!")` with: 40 | ```swift 41 | import Foundation 42 | import Kitura 43 | import KituraWebSocket 44 | import HeliumLogger 45 | 46 | // Using an implementation for a Logger 47 | HeliumLogger.use(.info) 48 | 49 | let router = Router() 50 | 51 | WebSocket.register(service: EchoService(), onPath: "/") 52 | 53 | let port = 9001 54 | 55 | Kitura.addHTTPServer(onPort: port, with: router) 56 | Kitura.run() 57 | ``` 58 | 5. Create the `EchoService` file 59 | ``` 60 | cd ~/WebSocketEchoServer/Sources/WebSocketEchoServer/ 61 | touch EchoService.swift 62 | open EchoService.swift 63 | ``` 64 | 6. Inside `EchoService` add the following code: 65 | ```swift 66 | import Foundation 67 | 68 | import KituraWebSocket 69 | import LoggerAPI 70 | 71 | class EchoService: WebSocketService { 72 | 73 | public func connected(connection: WebSocketConnection) {} 74 | 75 | public func disconnected(connection: WebSocketConnection, reason: WebSocketCloseReasonCode) {} 76 | 77 | public func received(message: Data, from: WebSocketConnection) { 78 | from.send(message: message) 79 | } 80 | 81 | public func received(message: String, from: WebSocketConnection) { 82 | Log.info("Got message \(message)... sending it back") 83 | from.send(message: message) 84 | } 85 | } 86 | ``` 87 | 7. Start the EchoServer: 88 | ``` 89 | cd ~/WebSocketEchoServer 90 | swift build 91 | .build/x86_64-apple-macosx10.10/debug/WebSocketEchoServer 92 | ``` 93 | 94 | ### Install and run the test suite 95 | Open a new terminal window and enter: 96 | ``` 97 | pip install autobahntestsuite 98 | cd ~ 99 | mkdir testAutobahn 100 | cd testAutobahn 101 | wstest -m fuzzingclient 102 | ``` 103 | 104 | This will run the autobahn tests against a server running on port 9001. 105 | To change the tests inside `testAutobahn`: 106 | `open fuzzingclient.json` 107 | 108 | In here you can select specific tests to include or exclude. 109 | 110 | To view the results `open ~/testAutobahn/reports/servers/index.html` 111 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | /** 5 | * Copyright IBM Corporation 2016, 2017 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | **/ 19 | 20 | import PackageDescription 21 | 22 | let package = Package( 23 | name: "Kitura-WebSocket", 24 | products: [ 25 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 26 | .library( 27 | name: "Kitura-WebSocket", 28 | targets: ["KituraWebSocket"]), 29 | ], 30 | dependencies: [ 31 | // Dependencies declare other packages that this package depends on. 32 | // .package(url: /* package url */, from: "1.0.0"), 33 | .package(url: "https://github.com/Kitura/Kitura-net.git", from: "2.4.200"), 34 | .package(url: "https://github.com/Kitura/BlueCryptor.git", from: "1.0.200"), 35 | 36 | ], 37 | targets: [ 38 | // Targets are the basic building blocks of a package. A target defines a module or a test suite. 39 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 40 | .target( 41 | name: "KituraWebSocket", 42 | dependencies: ["KituraNet", "Cryptor"]), 43 | .testTarget( 44 | name: "KituraWebSocketTests", 45 | dependencies: ["KituraWebSocket"]), 46 | ] 47 | ) 48 | -------------------------------------------------------------------------------- /Package@swift-4.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | /** 5 | * Copyright IBM Corporation and the Kitura project authors 2016-2020 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | **/ 19 | 20 | import PackageDescription 21 | 22 | let package = Package( 23 | name: "Kitura-WebSocket", 24 | products: [ 25 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 26 | .library( 27 | name: "Kitura-WebSocket", 28 | targets: ["KituraWebSocket"]), 29 | ], 30 | dependencies: [ 31 | // Dependencies declare other packages that this package depends on. 32 | // .package(url: /* package url */, from: "1.0.0"), 33 | .package(url: "https://github.com/Kitura/Kitura-net.git", from: "2.4.200"), 34 | .package(url: "https://github.com/Kitura/BlueCryptor.git", from: "1.0.200"), 35 | 36 | ], 37 | targets: [ 38 | // Targets are the basic building blocks of a package. A target defines a module or a test suite. 39 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 40 | .target( 41 | name: "KituraWebSocket", 42 | dependencies: ["KituraNet", "Cryptor"]), 43 | .testTarget( 44 | name: "KituraWebSocketTests", 45 | dependencies: ["KituraWebSocket"]), 46 | ] 47 | ) 48 | -------------------------------------------------------------------------------- /Sources/KituraWebSocket/WSConnectionUpgradeFactory.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright IBM Corporation 2016-2017 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | 19 | import Cryptor 20 | import KituraNet 21 | 22 | /// The implementation of the ConnectionUpgradeFactory protocol for the WebSocket protocol. 23 | /// Participates in the HTTP protocol upgrade process when upgarding to the WebSocket protocol. 24 | public class WSConnectionUpgradeFactory: ConnectionUpgradeFactory { 25 | private var registry = Dictionary() 26 | 27 | private let wsGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 28 | 29 | /// The name of the protocol supported by this `ConnectionUpgradeFactory`. 30 | public let name = "websocket" 31 | 32 | init() { 33 | ConnectionUpgrader.register(factory: self) 34 | } 35 | 36 | /// "Upgrade" a connection to the WebSocket protocol. Invoked by the KituraNet.ConnectionUpgrader when 37 | /// an upgrade request is being handled. 38 | /// 39 | /// - Parameter handler: The `IncomingSocketHandler` that is handling the connection being upgraded. 40 | /// - Parameter request: The `ServerRequest` object of the incoming "upgrade" request. 41 | /// - Parameter response: The `ServerResponse` object that will be used to send the response of the "upgrade" request. 42 | /// 43 | /// - Returns: A tuple of the created `WSSocketProcessor` and a message to send as the body of the response to 44 | /// the upgrade request. The `WSSocketProcessor` will be nil if the upgrade request wasn't successful. 45 | /// If the message is nil, the response will not contain a body. 46 | public func upgrade(handler: IncomingSocketHandler, request: ServerRequest, response: ServerResponse) -> (IncomingSocketProcessor?, String?) { 47 | 48 | guard let protocolVersion = request.headers["Sec-WebSocket-Version"] else { 49 | return (nil, "Sec-WebSocket-Version header missing in the upgrade request") 50 | } 51 | 52 | guard protocolVersion[0] == "13" else { 53 | response.headers["Sec-WebSocket-Version"] = ["13"] 54 | return (nil, "Only WebSocket protocol version 13 is supported") 55 | } 56 | 57 | guard let securityKey = request.headers["Sec-WebSocket-Key"] else { 58 | return (nil, "Sec-WebSocket-Key header missing in the upgrade request") 59 | } 60 | 61 | guard let service = registry[request.urlURL.path] else { 62 | return (nil, "No service has been registered for the path \(request.urlURL.path)") 63 | } 64 | 65 | let sha1 = Digest(using: .sha1) 66 | let sha1Bytes = sha1.update(string: securityKey[0] + wsGUID)!.final() 67 | let sha1Data = Data(bytes: sha1Bytes, count: sha1Bytes.count) 68 | response.headers["Sec-WebSocket-Accept"] = 69 | [sha1Data.base64EncodedString(options: .lineLength64Characters)] 70 | response.headers["Sec-WebSocket-Protocol"] = request.headers["Sec-WebSocket-Protocol"] 71 | 72 | let connection = WebSocketConnection(request: request, service: service) 73 | let processor = WSSocketProcessor(connection: connection) 74 | connection.processor = processor 75 | 76 | return (processor, nil) 77 | } 78 | 79 | func register(service: WebSocketService, onPath: String) { 80 | let path: String 81 | if onPath.hasPrefix("/") { 82 | path = onPath 83 | } 84 | else { 85 | path = "/" + onPath 86 | } 87 | registry[path] = service 88 | } 89 | 90 | func unregister(path thePath: String) { 91 | let path: String 92 | if thePath.hasPrefix("/") { 93 | path = thePath 94 | } 95 | else { 96 | path = "/" + thePath 97 | } 98 | registry.removeValue(forKey: path) 99 | } 100 | 101 | /// Clear the `WebSocketService` registry. Used in testing. 102 | func clear() { 103 | registry.removeAll() 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Sources/KituraWebSocket/WSFrame.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright IBM Corporation 2015 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | 19 | #if os(Linux) 20 | import Glibc 21 | #else 22 | import Darwin 23 | #endif 24 | 25 | struct WSFrame { 26 | var finalFrame = false 27 | 28 | enum FrameOpcode: Int { 29 | case continuation = 0, text = 1, binary = 2, close = 8, ping = 9, pong = 10, unknown = -1 30 | } 31 | var opCode = FrameOpcode.unknown 32 | 33 | let payload = NSMutableData() 34 | 35 | static func createFrameHeader(finalFrame: Bool, opCode: FrameOpcode, payloadLength: Int, buffer: NSMutableData) { 36 | 37 | var bytes: [UInt8] = [(finalFrame ? 0x80 : 0x00) | UInt8(opCode.rawValue), 0, 0,0,0,0,0,0,0,0] 38 | var length = 1 39 | 40 | if payloadLength < 126 { 41 | bytes[1] = UInt8(payloadLength) 42 | length += 1 43 | } else if payloadLength <= Int(UInt16.max) { 44 | bytes[1] = 126 45 | let tempPayloadLengh = UInt16(payloadLength) 46 | var payloadLengthUInt16: UInt16 47 | #if os(Linux) 48 | payloadLengthUInt16 = Glibc.htons(tempPayloadLengh) 49 | #else 50 | payloadLengthUInt16 = CFSwapInt16HostToBig(tempPayloadLengh) 51 | #endif 52 | let asBytes = UnsafeMutablePointer(&payloadLengthUInt16) 53 | #if swift(>=4.1) 54 | (UnsafeMutableRawPointer(mutating: bytes)+length+1).copyMemory(from: asBytes, byteCount: 2) 55 | #else 56 | (UnsafeMutableRawPointer(mutating: bytes)+length+1).copyBytes(from: asBytes, count: 2) 57 | #endif 58 | length += 3 59 | } else { 60 | bytes[1] = 127 61 | let tempPayloadLengh = UInt32(payloadLength) 62 | var payloadLengthUInt32: UInt32 63 | #if os(Linux) 64 | payloadLengthUInt32 = Glibc.htonl(tempPayloadLengh) 65 | #else 66 | payloadLengthUInt32 = CFSwapInt32HostToBig(tempPayloadLengh) 67 | #endif 68 | let asBytes = UnsafeMutablePointer(&payloadLengthUInt32) 69 | #if swift(>=4.1) 70 | (UnsafeMutableRawPointer(mutating: bytes)+length+5).copyMemory(from: asBytes, byteCount: 4) 71 | #else 72 | (UnsafeMutableRawPointer(mutating: bytes)+length+5).copyBytes(from: asBytes, count: 4) 73 | #endif 74 | length += 9 75 | } 76 | buffer.append(bytes, length: length) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/KituraWebSocket/WSFrameParser.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright IBM Corporation 2015 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | 19 | #if os(Linux) 20 | import Glibc 21 | #else 22 | import Darwin 23 | #endif 24 | 25 | struct WSFrameParser { 26 | 27 | private enum FrameParsingState { 28 | case initial, opCodeParsed, lengthParsed 29 | } 30 | private var state = FrameParsingState.initial 31 | 32 | var frame = WSFrame() 33 | 34 | var masked = false 35 | private var payloadLength = -1 36 | private var mask: [UInt8] = [0x00, 0x00, 0x00, 0x00] 37 | private let maskSize = 4 38 | 39 | mutating func reset() { 40 | state = .initial 41 | payloadLength = -1 42 | frame.payload.length = 0 43 | } 44 | 45 | mutating func parse(_ buffer: NSData, from: Int) -> (Bool, WebSocketError?, Int) { 46 | var bytesParsed = 0 47 | var byteIndex = from 48 | let bytes = UnsafeRawBufferPointer(start: buffer.bytes, count: buffer.length) 49 | 50 | let length = buffer.length 51 | 52 | while byteIndex < length && payloadLength != frame.payload.length { 53 | switch(state) { 54 | case .initial: 55 | let (error, bytesConsumed) = parseOpCode(bytes: bytes, from: byteIndex) 56 | guard error == nil else { return (false, error, 0) } 57 | 58 | bytesParsed += bytesConsumed 59 | byteIndex += bytesConsumed 60 | state = .opCodeParsed 61 | 62 | case .opCodeParsed: 63 | let (error, bytesConsumed) = parseMaskAndLength(bytes: bytes, from: byteIndex, length: length) 64 | guard error == nil else { return (false, error, 0) } 65 | guard bytesConsumed > 0 else { return (false, error, bytesParsed) } 66 | 67 | bytesParsed += bytesConsumed 68 | byteIndex += bytesConsumed 69 | state = .lengthParsed 70 | 71 | case .lengthParsed: 72 | let bytesConsumed = parsePayload(bytes: bytes, from: byteIndex, length: length) 73 | 74 | bytesParsed += bytesConsumed 75 | byteIndex += bytesConsumed 76 | } 77 | } 78 | 79 | return (payloadLength == frame.payload.length, nil, bytesParsed) 80 | } 81 | 82 | private mutating func parseOpCode(bytes: UnsafeRawBufferPointer, from: Int) -> (WebSocketError?, Int) { 83 | let byte = bytes[from] 84 | // 0x.. is the hexadecimal representation of the bits you would like to check 85 | // e.g.0x0f is 15 which is 00001111 so rawOpCode is the last 4 digits 86 | 87 | // Check RSV (three reserved bits) is equal to 0 88 | let rsv = (byte & 0x70) >> 4 89 | guard rsv == 0 else { 90 | return (.invalidOpCode(byte & 0x7F), 0) 91 | } 92 | frame.finalFrame = byte & 0x80 != 0 93 | let rawOpCode = byte & 0x0f 94 | if let opCode = WSFrame.FrameOpcode(rawValue: Int(rawOpCode)) { 95 | self.frame.opCode = opCode 96 | return (nil, 1) 97 | } 98 | else { 99 | return (.invalidOpCode(rawOpCode), 0) 100 | } 101 | } 102 | 103 | private mutating func parseMaskAndLength(bytes: UnsafeRawBufferPointer, from: Int, length: Int) -> (WebSocketError?, Int) { 104 | let byte = bytes[from] 105 | masked = byte & 0x80 != 0 106 | 107 | guard masked else { return (.unmaskedFrame, 0) } 108 | 109 | var bytesConsumed = 0 110 | 111 | let lengthByte = byte & 0x7f 112 | switch lengthByte { 113 | case 126: 114 | if length - from >= 3 { 115 | // We cannot perform an unaligned load of a 16-bit value from the buffer. 116 | // Instead, create correctly aligned storage for a 16-bit value and copy 117 | // the bytes from the buffer into its storage. 118 | var networkOrderedUInt16 = UInt16(0) 119 | withUnsafeMutableBytes(of: &networkOrderedUInt16) { ptr in 120 | let unalignedUInt16 = UnsafeRawBufferPointer(rebasing: bytes[from+1 ..< from+3]) 121 | #if swift(>=4.1) 122 | ptr.copyMemory(from: unalignedUInt16) 123 | #else 124 | ptr.copyBytes(from: unalignedUInt16) 125 | #endif 126 | } 127 | #if os(Linux) 128 | payloadLength = Int(Glibc.ntohs(networkOrderedUInt16)) 129 | #else 130 | payloadLength = Int(CFSwapInt16BigToHost(networkOrderedUInt16)) 131 | #endif 132 | bytesConsumed += 3 133 | } 134 | case 127: 135 | if length - from >= 9 { 136 | // We cannot perform an unaligned load of a 32-bit value from the buffer. 137 | // Instead, create correctly aligned storage for a 32-bit value and copy 138 | // the bytes from the buffer into its storage. 139 | var networkOrderedUInt32 = UInt32(0) 140 | withUnsafeMutableBytes(of: &networkOrderedUInt32) { ptr in 141 | let unalignedUInt32 = UnsafeRawBufferPointer(rebasing: bytes[from+5 ..< from+9]) 142 | #if swift(>=4.1) 143 | ptr.copyMemory(from: unalignedUInt32) 144 | #else 145 | ptr.copyBytes(from: unalignedUInt32) 146 | #endif 147 | } 148 | #if os(Linux) 149 | payloadLength = Int(Glibc.ntohl(networkOrderedUInt32)) 150 | #else 151 | payloadLength = Int(CFSwapInt32BigToHost(networkOrderedUInt32)) 152 | #endif 153 | bytesConsumed += 9 154 | } 155 | /* Error if length > Int.max */ 156 | default: 157 | payloadLength = Int(lengthByte) 158 | bytesConsumed += 1 159 | } 160 | 161 | if bytesConsumed > 0 { 162 | if length - from - bytesConsumed >= maskSize { 163 | for i in 0.. Int { 179 | //let payloadPiece = bytes + from 180 | var unmaskedBytes = [UInt8](repeating: 0, count: 125) 181 | var bytesConsumed: Int = 0 182 | 183 | let bytesToUnmask = min(payloadLength - frame.payload.length, length - from) 184 | var bytesUnmasked = frame.payload.length 185 | 186 | // Loop to unmask the bytes we have in this frame piece 187 | while bytesConsumed < bytesToUnmask { 188 | 189 | let bytesToUnMaskInLoop = min(unmaskedBytes.count, bytesToUnmask-bytesConsumed) 190 | 191 | for index in 0 ..< bytesToUnMaskInLoop { 192 | unmaskedBytes[index] = bytes[from + bytesConsumed] ^ mask[bytesUnmasked % maskSize] 193 | bytesUnmasked += 1 194 | bytesConsumed += 1 195 | } 196 | frame.payload.append(unmaskedBytes, length: bytesToUnMaskInLoop) 197 | } 198 | return bytesConsumed 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Sources/KituraWebSocket/WSServerRequest.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright IBM Corporation 2017 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | 19 | import KituraNet 20 | 21 | /// An internal class used to retain information from the original request that 22 | /// was used to create the WebSocket connection. The ServerRequest from KituraNet 23 | /// may get freed. 24 | class WSServerRequest: ServerRequest { 25 | 26 | /// The set of headers received with the incoming request 27 | let headers = HeadersContainer() 28 | 29 | /// The URL from the request in string form 30 | /// This contains just the path and query parameters starting with '/' 31 | /// Use 'urlURL' for the full URL 32 | @available(*, deprecated, message: 33 | "This contains just the path and query parameters starting with '/'. use 'urlURL' instead") 34 | var urlString: String { return String(data: url, encoding: .utf8) ?? "" } 35 | 36 | /// The URL from the request in UTF-8 form 37 | /// This contains just the path and query parameters starting with '/' 38 | /// Use 'urlURL' for the full URL 39 | let url: Data 40 | 41 | /// The URL from the request 42 | let urlURL: URL 43 | 44 | /// The URL from the request as URLComponents 45 | public let urlComponents: URLComponents 46 | 47 | /// The IP address of the client 48 | let remoteAddress: String 49 | 50 | /// Major version of HTTP of the request 51 | var httpVersionMajor: UInt16? = 1 52 | 53 | /// Minor version of HTTP of the request 54 | var httpVersionMinor: UInt16? = 1 55 | 56 | /// The HTTP Method specified in the request 57 | var method: String = "GET" 58 | 59 | init(request: ServerRequest) { 60 | for (key, values) in request.headers { 61 | headers.append(key, value: values) 62 | } 63 | 64 | url = request.url 65 | urlURL = request.urlURL 66 | urlComponents = URLComponents(url: request.urlURL, resolvingAgainstBaseURL: false) ?? URLComponents() 67 | 68 | remoteAddress = request.remoteAddress 69 | } 70 | 71 | /// Read data from the body of the request 72 | /// 73 | /// - Parameter data: A Data struct to hold the data read in. 74 | /// 75 | /// - Throws: Socket.error if an error occurred while reading from the socket 76 | /// - Returns: The number of bytes read 77 | func read(into data: inout Data) throws -> Int { 78 | return 0 79 | } 80 | 81 | /// Read a string from the body of the request. 82 | /// 83 | /// - Throws: Socket.error if an error occurred while reading from the socket 84 | /// - Returns: An Optional string 85 | func readString() throws -> String? { 86 | return nil 87 | } 88 | 89 | 90 | /// Read all of the data in the body of the request 91 | /// 92 | /// - Parameter data: A Data struct to hold the data read in. 93 | /// 94 | /// - Throws: Socket.error if an error occurred while reading from the socket 95 | /// - Returns: The number of bytes read 96 | func readAllData(into data: inout Data) throws -> Int { 97 | return 0 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Sources/KituraWebSocket/WSSocketProcessor.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright IBM Corporation 2016-2017 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | 19 | import KituraNet 20 | import LoggerAPI 21 | 22 | /// The implementation of the `IncomingSocketProcessor` protocol for the WebSocket protocol. 23 | /// Receives data from the `IncomingSocketHandler` for a specific socket and provides APIs 24 | /// upwards for sending data to the client over the socket. 25 | class WSSocketProcessor: IncomingSocketProcessor { 26 | /// A back reference to the `IncomingSocketHandler` processing the socket that 27 | /// this `IncomingDataProcessor` is processing. 28 | public weak var handler: IncomingSocketHandler? { 29 | didSet { 30 | guard handler != nil else { return } 31 | connection.fireConnected() 32 | } 33 | } 34 | 35 | /// The socket if idle will be kept alive until... 36 | public var keepAliveUntil: TimeInterval = 500.0 37 | 38 | /// A flag to indicate that the socket has a request in progress 39 | public var inProgress = true 40 | 41 | private var parser = WSFrameParser() 42 | 43 | private var byteIndex = 0 44 | 45 | private let connection: WebSocketConnection 46 | 47 | init(connection: WebSocketConnection) { 48 | self.connection = connection 49 | } 50 | 51 | /// Process data read from the socket. 52 | /// 53 | /// - Parameter buffer: An NSData object containing the data that was read in 54 | /// and needs to be processed. 55 | /// 56 | /// - Returns: true if the data was processed, false if it needs to be processed later. 57 | public func process(_ buffer: NSData) -> Bool { 58 | let length = buffer.length 59 | 60 | while byteIndex < length { 61 | let (completed, error, bytesConsumed) = parser.parse(buffer, from: byteIndex) 62 | 63 | guard error == nil else { 64 | // What should be done if there is an error? 65 | Log.error("Error parsing frame. \(error!)") 66 | connection.close(reason: .protocolError, description: error?.description) 67 | return true 68 | } 69 | 70 | if bytesConsumed == 0 { 71 | break 72 | } 73 | 74 | byteIndex += bytesConsumed 75 | 76 | if completed { 77 | connection.received(frame: parser.frame) 78 | parser.reset() 79 | } 80 | } 81 | 82 | let finishedBuffer: Bool 83 | if byteIndex >= length { 84 | finishedBuffer = true 85 | byteIndex = 0 86 | } 87 | else { 88 | finishedBuffer = false 89 | } 90 | return finishedBuffer 91 | } 92 | 93 | /// Write data to the socket 94 | /// 95 | /// - Parameter from: An NSData object containing the bytes to be written to the socket. 96 | public func write(from data: NSData) { 97 | handler?.write(from: data) 98 | } 99 | 100 | /// Write a sequence of bytes in an array to the socket 101 | /// 102 | /// - Parameter from: An UnsafeRawPointer to the sequence of bytes to be written to the socket. 103 | /// - Parameter length: The number of bytes to write to the socket. 104 | public func write(from bytes: UnsafeRawPointer, length: Int) { 105 | handler?.write(from: bytes, length: length) 106 | } 107 | 108 | /// Close the socket and mark this handler as no longer in progress. 109 | public func close() { 110 | if connection.active { 111 | connection.connectionClosed(reason: .closedAbnormally) 112 | } 113 | handler?.prepareToClose() 114 | } 115 | 116 | /// Called by the `IncomingSocketHandler` to tell us that the socket has been closed. 117 | public func socketClosed() { 118 | connection.connectionClosed(reason: .noReasonCodeSent) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Sources/KituraWebSocket/WebSocket.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright IBM Corporation 2016, 2017 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import KituraNet 18 | 19 | /// Main class for the Kitura-WebSocket API. Used to register `WebSocketService` classes 20 | /// that will handle WebSocket connections for specific paths. 21 | public class WebSocket { 22 | static let factory = WSConnectionUpgradeFactory() 23 | 24 | /// Register a `WebSocketService` for a specific path 25 | /// 26 | /// - Parameter service: The `WebSocketService` being registered. 27 | /// - Parameter onPath: The path that will be in the HTTP "Upgrade" request. Used 28 | /// to connect the upgrade request with a specific `WebSocketService` 29 | /// Caps-insensitive. 30 | public static func register(service: WebSocketService, onPath path: String) { 31 | factory.register(service: service, onPath: path.lowercased()) 32 | } 33 | 34 | /// Unregister a `WebSocketService` for a specific path 35 | /// 36 | /// - Parameter path: The path on which the `WebSocketService` being unregistered, 37 | /// was registered on. 38 | public static func unregister(path: String) { 39 | factory.unregister(path: path.lowercased()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/KituraWebSocket/WebSocketCloseReasonCode.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright IBM Corporation 2015 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /// The `WebSocketCloseReasonCode` enum defines the set of reason codes that a 18 | /// WebSocket application can send/receive when a connection is closed. 19 | public enum WebSocketCloseReasonCode { 20 | /// Closed abnormally (1006) 21 | case closedAbnormally 22 | 23 | /// An extension was missing that was required (1010) 24 | case extensionMissing 25 | 26 | /// Server is going away (1001) 27 | case goingAway 28 | 29 | /// Data within a message was invalid (1007) 30 | case invalidDataContents 31 | 32 | /// Message was of the incorrect type (binary/text) (1003) 33 | case invalidDataType 34 | 35 | /// Message was too large (1009) 36 | case messageTooLarge 37 | 38 | /// Closed normally (1000) 39 | case normal 40 | 41 | /// No reason code sent with the close request (1005) 42 | case noReasonCodeSent 43 | 44 | /// A policy violation occurred (1008) 45 | case policyViolation 46 | 47 | /// A protocol error occurred (1002) 48 | case protocolError 49 | 50 | /// The server had an error with the request (1011) 51 | case serverError 52 | 53 | /// This reason code is used to send application defined reason codes. 54 | case userDefined(UInt16) 55 | 56 | /// Get the sixteen bit integer code for a WebSocketCloseReasonCode instance 57 | public func code() -> UInt16 { 58 | switch self { 59 | case .closedAbnormally: return 1006 60 | case .extensionMissing: return 1010 61 | case .goingAway: return 1001 62 | case .invalidDataContents: return 1007 63 | case .invalidDataType: return 1003 64 | case .messageTooLarge: return 1009 65 | case .normal: return 1000 66 | case .noReasonCodeSent: return 1005 67 | case .policyViolation: return 1008 68 | case .protocolError: return 1002 69 | case .serverError: return 1011 70 | case .userDefined(let userCode): return userCode 71 | } 72 | } 73 | 74 | /// Convert a sixteen bit WebSocket close frame reason code to a WebSocketCloseReasonCode instance 75 | public static func from(code reasonCode: UInt16) -> WebSocketCloseReasonCode { 76 | switch reasonCode { 77 | case 1000: return .normal 78 | case 1001: return .goingAway 79 | case 1002: return .protocolError 80 | case 1003: return .invalidDataType 81 | case 1007: return .invalidDataContents 82 | case 1008: return .policyViolation 83 | case 1009: return .messageTooLarge 84 | case 1010: return .extensionMissing 85 | case 1011: return .serverError 86 | default: 87 | if reasonCode < 3000 { 88 | return .protocolError 89 | } else { 90 | return .userDefined(reasonCode) 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Sources/KituraWebSocket/WebSocketError.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright IBM Corporation 2016 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | 19 | /// An error enum used when throwing errors within KituraWebSocket. 20 | public enum WebSocketError: Error { 21 | 22 | /// An invalid opcode was received in a WebSocket frame 23 | case invalidOpCode(UInt8) 24 | 25 | /// A frame was received that had an unmasked payload 26 | case unmaskedFrame 27 | } 28 | 29 | 30 | extension WebSocketError: CustomStringConvertible { 31 | /// Generate a printable version of this enum. 32 | public var description: String { 33 | switch self { 34 | case .invalidOpCode(let opCode): 35 | return "Parsed a frame with an invalid operation code of \(opCode)" 36 | case .unmaskedFrame: 37 | return "Received a frame from a client that wasn't masked" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/KituraWebSocket/WebSocketService.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright IBM Corporation 2016-2017 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | 19 | /// The `WebSocketService` protocol is implemented by classes that wish to be WebSocket server side 20 | /// end points. An instance of the `WebSocketService` protocol is the server side of a WebSocket connection. 21 | /// There can be many WebSocket connections connected to a single `WebSocketService` protocol instance. 22 | /// The protocol is a set of callbacks that are invoked when various events occur. 23 | public protocol WebSocketService: class { 24 | 25 | /// Called when a WebSocket client connects to the server and is connected to a specific 26 | /// `WebSocketService`. 27 | /// 28 | /// - Parameter connection: The `WebSocketConnection` object that represents the client's 29 | /// connection to this `WebSocketService` 30 | func connected(connection: WebSocketConnection) 31 | 32 | /// Called when a WebSocket client disconnects from the server. 33 | /// 34 | /// - Parameter connection: The `WebSocketConnection` object that represents the connection that 35 | /// was disconnected from this `WebSocketService`. 36 | /// - Parameter reason: The `WebSocketCloseReasonCode` that describes why the client disconnected. 37 | func disconnected(connection: WebSocketConnection, reason: WebSocketCloseReasonCode) 38 | 39 | /// Called when a WebSocket client sent a binary message to the server to this `WebSocketService`. 40 | /// 41 | /// - Parameter message: A Data struct containing the bytes of the binary message sent by the client. 42 | /// - Parameter client: The `WebSocketConnection` object that represents the connection over which 43 | /// the client sent the message to this `WebSocketService` 44 | func received(message: Data, from: WebSocketConnection) 45 | 46 | /// Called when a WebSocket client sent a text message to the server to this `WebSocketService`. 47 | /// 48 | /// - Parameter message: A String containing the text message sent by the client. 49 | /// - Parameter client: The `WebSocketConnection` object that represents the connection over which 50 | /// the client sent the message to this `WebSocketService` 51 | func received(message: String, from: WebSocketConnection) 52 | 53 | /// 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. 54 | var connectionTimeout: Int? { get } 55 | } 56 | 57 | public extension WebSocketService { 58 | /// Default computed value for `connectionTimeout` that returns `nil`. 59 | var connectionTimeout: Int? { 60 | return nil 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tests/KituraWebSocketTests/ComplexTests.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2016 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | import XCTest 18 | import Foundation 19 | 20 | import LoggerAPI 21 | @testable import KituraWebSocket 22 | 23 | class ComplexTests: KituraTest { 24 | 25 | static var allTests: [(String, (ComplexTests) -> () throws -> Void)] { 26 | return [ 27 | ("testBinaryShortAndMediumFrames", testBinaryShortAndMediumFrames), 28 | ("testBinaryTwoShortFrames", testBinaryTwoShortFrames), 29 | ("testPingBetweenBinaryFrames", testPingBetweenBinaryFrames), 30 | ("testPingBetweenTextFrames", testPingBetweenTextFrames), 31 | ("testTextShortAndMediumFrames", testTextShortAndMediumFrames), 32 | ("testTextTwoShortFrames", testTextTwoShortFrames) 33 | ] 34 | } 35 | 36 | func testBinaryShortAndMediumFrames() { 37 | register(closeReason: .noReasonCodeSent) 38 | 39 | performServerTest() { expectation in 40 | 41 | var bytes = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e] 42 | 43 | let shortBinaryPayload = NSMutableData(bytes: &bytes, length: bytes.count) 44 | 45 | let mediumBinaryPayload = NSMutableData(bytes: &bytes, length: bytes.count) 46 | repeat { 47 | mediumBinaryPayload.append(mediumBinaryPayload.bytes, length: mediumBinaryPayload.length) 48 | } while mediumBinaryPayload.length < 1000 49 | 50 | let expectedBinaryPayload = NSMutableData() 51 | expectedBinaryPayload.append(shortBinaryPayload.bytes, length: shortBinaryPayload.length) 52 | expectedBinaryPayload.append(mediumBinaryPayload.bytes, length: mediumBinaryPayload.length) 53 | 54 | self.performTest(framesToSend: [(false, self.opcodeBinary, shortBinaryPayload), (true, self.opcodeContinuation, mediumBinaryPayload)], 55 | expectedFrames: [(true, self.opcodeBinary, expectedBinaryPayload)], 56 | expectation: expectation) 57 | } 58 | } 59 | 60 | func testBinaryTwoShortFrames() { 61 | register(closeReason: .noReasonCodeSent) 62 | 63 | performServerTest() { expectation in 64 | 65 | var bytes = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e] 66 | 67 | let binaryPayload = NSMutableData(bytes: &bytes, length: bytes.count) 68 | 69 | let expectedBinaryPayload = NSMutableData(bytes: &bytes, length: bytes.count) 70 | expectedBinaryPayload.append(&bytes, length: bytes.count) 71 | 72 | self.performTest(framesToSend: [(false, self.opcodeBinary, binaryPayload), (true, self.opcodeContinuation, binaryPayload)], 73 | expectedFrames: [(true, self.opcodeBinary, expectedBinaryPayload)], 74 | expectation: expectation) 75 | } 76 | } 77 | 78 | func testPingBetweenBinaryFrames() { 79 | register(closeReason: .noReasonCodeSent) 80 | 81 | performServerTest() { expectation in 82 | 83 | var bytes = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e] 84 | 85 | let binaryPayload = NSMutableData(bytes: &bytes, length: bytes.count) 86 | 87 | let expectedBinaryPayload = NSMutableData(bytes: &bytes, length: bytes.count) 88 | expectedBinaryPayload.append(&bytes, length: bytes.count) 89 | 90 | let pingPayload = self.payload(text: "Testing, testing 1,2,3") 91 | 92 | self.performTest(framesToSend: [(false, self.opcodeBinary, binaryPayload), 93 | (true, self.opcodePing, pingPayload), 94 | (true, self.opcodeContinuation, binaryPayload)], 95 | expectedFrames: [(true, self.opcodePong, pingPayload), (true, self.opcodeBinary, expectedBinaryPayload)], 96 | expectation: expectation) 97 | } 98 | } 99 | 100 | func testPingBetweenTextFrames() { 101 | register(closeReason: .noReasonCodeSent) 102 | 103 | performServerTest() { expectation in 104 | 105 | let text = "Testing, testing 1, 2, 3. " 106 | 107 | let textPayload = self.payload(text: text) 108 | 109 | let textExpectedPayload = self.payload(text: text + text) 110 | 111 | let pingPayload = self.payload(text: "Testing, testing 1,2,3") 112 | 113 | self.performTest(framesToSend: [(false, self.opcodeText, textPayload), 114 | (true, self.opcodePing, pingPayload), 115 | (true, self.opcodeContinuation, textPayload)], 116 | expectedFrames: [(true, self.opcodePong, pingPayload), (true, self.opcodeText, textExpectedPayload)], 117 | expectation: expectation) 118 | } 119 | } 120 | 121 | func testTextShortAndMediumFrames() { 122 | register(closeReason: .noReasonCodeSent) 123 | 124 | performServerTest() { expectation in 125 | 126 | let shortText = "Testing, testing 1, 2, 3. " 127 | let shortTextPayload = self.payload(text: shortText) 128 | 129 | var mediumText = "" 130 | repeat { 131 | mediumText += "Testing, testing 1,2,3. " 132 | } while mediumText.count < 1000 133 | let mediumTextPayload = self.payload(text: mediumText) 134 | 135 | let textExpectedPayload = self.payload(text: shortText + mediumText) 136 | 137 | self.performTest(framesToSend: [(false, self.opcodeText, shortTextPayload), (true, self.opcodeContinuation, mediumTextPayload)], 138 | expectedFrames: [(true, self.opcodeText, textExpectedPayload)], 139 | expectation: expectation) 140 | } 141 | } 142 | 143 | func testTextTwoShortFrames() { 144 | register(closeReason: .noReasonCodeSent) 145 | 146 | performServerTest() { expectation in 147 | 148 | let text = "Testing, testing 1, 2, 3. " 149 | 150 | let textPayload = self.payload(text: text) 151 | 152 | let textExpectedPayload = self.payload(text: text + text) 153 | 154 | self.performTest(framesToSend: [(false, self.opcodeText, textPayload), (true, self.opcodeContinuation, textPayload)], 155 | expectedFrames: [(true, self.opcodeText, textExpectedPayload)], 156 | expectation: expectation) 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Tests/KituraWebSocketTests/ConnectionCleanupTests.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2016 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | import XCTest 18 | import Foundation 19 | 20 | import LoggerAPI 21 | @testable import KituraWebSocket 22 | import Socket 23 | 24 | class ConnectionCleanupTests: KituraTest { 25 | 26 | static var allTests: [(String, (ConnectionCleanupTests) -> () throws -> Void)] { 27 | return [ 28 | ("testNilConnectionTimeOut", testNilConnectionTimeOut), 29 | ("testSingleConnectionTimeOut", testSingleConnectionTimeOut), 30 | ("testPingKeepsConnectionAlive", testPingKeepsConnectionAlive), 31 | ("testMultiConnectionTimeOut", testMultiConnectionTimeOut), 32 | ("testProccessorClose", testProccessorClose), 33 | ] 34 | } 35 | 36 | func testNilConnectionTimeOut() { 37 | let service = register(closeReason: .noReasonCodeSent, connectionTimeout: nil) 38 | 39 | performServerTest() { expectation in 40 | XCTAssertEqual(service.connections.count, 0, "Connections left on service at start of test") 41 | guard let socket = self.sendUpgradeRequest(toPath: self.servicePath, usingKey: self.secWebKey) else { return } 42 | let _ = self.checkUpgradeResponse(from: socket, forKey: self.secWebKey) 43 | usleep(5000) 44 | XCTAssertEqual(service.connections.count, 1, "Failed to create connection to service") 45 | usleep(1500000) 46 | XCTAssertEqual(service.connections.count, 1, "Stale connection was unexpectedly cleaned up") 47 | socket.close() 48 | usleep(1000) 49 | expectation.fulfill() 50 | } 51 | } 52 | 53 | func testSingleConnectionTimeOut() { 54 | let service = register(closeReason: .closedAbnormally, connectionTimeout: 1) 55 | 56 | performServerTest() { expectation in 57 | XCTAssertEqual(service.connections.count, 0, "Connections left on service at start of test") 58 | guard let socket = self.sendUpgradeRequest(toPath: self.servicePath, usingKey: self.secWebKey) else { return } 59 | let _ = self.checkUpgradeResponse(from: socket, forKey: self.secWebKey) 60 | usleep(5000) 61 | XCTAssertEqual(service.connections.count, 1, "Failed to create connection to service") 62 | usleep(1500000) 63 | XCTAssertEqual(service.connections.count, 0, "Stale connection was not cleaned up") 64 | socket.close() 65 | usleep(1000) 66 | expectation.fulfill() 67 | } 68 | } 69 | 70 | func testPingKeepsConnectionAlive() { 71 | let service = register(closeReason: .noReasonCodeSent, connectionTimeout: 1) 72 | 73 | performServerTest() { expectation in 74 | XCTAssertEqual(service.connections.count, 0, "Connections left on service at start of test") 75 | guard let socket = self.sendUpgradeRequest(toPath: self.servicePath, usingKey: self.secWebKey) else { return } 76 | let _ = self.checkUpgradeResponse(from: socket, forKey: self.secWebKey) 77 | usleep(5000) 78 | XCTAssertEqual(service.connections.count, 1, "Failed to create connection to service") 79 | usleep(500000) 80 | self.sendFrame(final: true, withOpcode: self.opcodePing, withPayload: NSData(), on: socket) 81 | usleep(500000) 82 | self.sendFrame(final: true, withOpcode: self.opcodePing, withPayload: NSData(), on: socket) 83 | usleep(500000) 84 | XCTAssertEqual(service.connections.count, 1, "Stale connection was unexpectedly cleaned up") 85 | self.sendFrame(final: true, withOpcode: self.opcodeClose, withPayload: NSData(), on: socket) 86 | usleep(500000) 87 | XCTAssertEqual(service.connections.count, 0, "Connection was not removed even after getting a close opcode") 88 | socket.close() 89 | usleep(1000) 90 | expectation.fulfill() 91 | } 92 | } 93 | 94 | func testMultiConnectionTimeOut() { 95 | let service = register(closeReason: .closedAbnormally, connectionTimeout: 1) 96 | 97 | performServerTest() { expectation in 98 | XCTAssertEqual(service.connections.count, 0, "Connections left on service at start of test") 99 | guard let socket = self.sendUpgradeRequest(toPath: self.servicePath, usingKey: self.secWebKey) else { return } 100 | let _ = self.checkUpgradeResponse(from: socket, forKey: self.secWebKey) 101 | usleep(5000) 102 | XCTAssertEqual(service.connections.count, 1, "Failed to create connection to service") 103 | guard let socket2 = self.sendUpgradeRequest(toPath: self.servicePath, usingKey: self.secWebKey) else { return } 104 | let _ = self.checkUpgradeResponse(from: socket2, forKey: self.secWebKey) 105 | usleep(5000) 106 | XCTAssertEqual(service.connections.count, 2, "Failed to create second connection to service") 107 | usleep(500000) 108 | self.sendFrame(final: true, withOpcode: self.opcodePing, withPayload: NSData(), on: socket) 109 | usleep(500000) 110 | self.sendFrame(final: true, withOpcode: self.opcodePing, withPayload: NSData(), on: socket) 111 | usleep(500000) 112 | self.sendFrame(final: true, withOpcode: self.opcodePing, withPayload: NSData(), on: socket) 113 | XCTAssertEqual(service.connections.count, 1, "Stale connection was not cleaned up") 114 | socket.close() 115 | socket2.close() 116 | usleep(1000) 117 | expectation.fulfill() 118 | } 119 | } 120 | 121 | func testProccessorClose() { 122 | let service = register(closeReason: .closedAbnormally, connectionTimeout: nil) 123 | 124 | performServerTest() { expectation in 125 | XCTAssertEqual(service.connections.count, 0, "Connections left on service at start of test") 126 | guard let socket = self.sendUpgradeRequest(toPath: self.servicePath, usingKey: self.secWebKey) else { return } 127 | let _ = self.checkUpgradeResponse(from: socket, forKey: self.secWebKey) 128 | usleep(5000) 129 | XCTAssertEqual(service.connections.count, 1, "Failed to create connection to service") 130 | let connections = Array(service.connections.values) 131 | connections[0].processor?.close() 132 | usleep(5000) 133 | XCTAssertEqual(service.connections.count, 0, "Service was not notified of connection disconnect") 134 | socket.close() 135 | usleep(1000) 136 | expectation.fulfill() 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Tests/KituraWebSocketTests/PrintLogger.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2016 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | import Foundation 18 | 19 | import LoggerAPI 20 | 21 | /// The set of colors used when logging with colorized lines 22 | public enum TerminalColor: String { 23 | /// Log text in white. 24 | case white = "\u{001B}[0;37m" // white 25 | /// Log text in red, used for error messages. 26 | case red = "\u{001B}[0;31m" // red 27 | /// Log text in yellow, used for warning messages. 28 | case yellow = "\u{001B}[0;33m" // yellow 29 | /// Log text in the terminal's default foreground color. 30 | case foreground = "\u{001B}[0;39m" // default foreground color 31 | /// Log text in the terminal's default background color. 32 | case background = "\u{001B}[0;49m" // default background color 33 | } 34 | 35 | public class PrintLogger: Logger { 36 | let colored: Bool 37 | 38 | init(colored: Bool) { 39 | self.colored = colored 40 | } 41 | 42 | public func log(_ type: LoggerMessageType, msg: String, 43 | functionName: String, lineNum: Int, fileName: String ) { 44 | let message = "[\(type)] [\(getFile(fileName)):\(lineNum) \(functionName)] \(msg)" 45 | 46 | guard colored else { 47 | print(message) 48 | return 49 | } 50 | 51 | let color : TerminalColor 52 | switch type { 53 | case .warning: 54 | color = .yellow 55 | case .error: 56 | color = .red 57 | default: 58 | color = .foreground 59 | } 60 | 61 | print(color.rawValue + message + TerminalColor.foreground.rawValue) 62 | } 63 | 64 | public func isLogging(_ level: LoggerAPI.LoggerMessageType) -> Bool { 65 | return true 66 | } 67 | 68 | public static func use(colored: Bool) { 69 | Log.logger = PrintLogger(colored: colored) 70 | setbuf(stdout, nil) 71 | } 72 | 73 | private func getFile(_ path: String) -> String { 74 | guard let range = path.range(of: "/", options: .backwards) else { 75 | return path 76 | } 77 | #if swift(>=3.2) 78 | return String(path[range.upperBound...]) 79 | #else 80 | return path.substring(from: range.upperBound) 81 | #endif 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Tests/KituraWebSocketTests/TestLinuxSafeguard.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2017 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #if os(OSX) && !swift(>=3.2) 18 | import XCTest 19 | 20 | class TestLinuxSafeguard: XCTestCase { 21 | func testVerifyLinuxTestCount() { 22 | var linuxCount: Int 23 | var darwinCount: Int 24 | 25 | // BasicTests 26 | linuxCount = BasicTests.allTests.count 27 | darwinCount = Int(BasicTests.defaultTestSuite().testCaseCount) 28 | XCTAssertEqual(linuxCount, darwinCount, "\(darwinCount - linuxCount) tests are missing from BasicTests.allTests") 29 | 30 | // ComplexTests 31 | linuxCount = ComplexTests.allTests.count 32 | darwinCount = Int(ComplexTests.defaultTestSuite().testCaseCount) 33 | XCTAssertEqual(linuxCount, darwinCount, "\(darwinCount - linuxCount) tests are missing from ComplexTests.allTests") 34 | 35 | // ProtocolErrorTests 36 | linuxCount = ProtocolErrorTests.allTests.count 37 | darwinCount = Int(ProtocolErrorTests.defaultTestSuite().testCaseCount) 38 | XCTAssertEqual(linuxCount, darwinCount, "\(darwinCount - linuxCount) tests are missing from ProtocolErrorTests.allTests") 39 | 40 | // UpgradeErrors 41 | linuxCount = UpgradeErrors.allTests.count 42 | darwinCount = Int(UpgradeErrors.defaultTestSuite().testCaseCount) 43 | XCTAssertEqual(linuxCount, darwinCount, "\(darwinCount - linuxCount) tests are missing from UpgradeErrors.allTests") 44 | } 45 | } 46 | #endif 47 | -------------------------------------------------------------------------------- /Tests/KituraWebSocketTests/TestWebSocketService.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2016-2017 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | import XCTest 18 | import Foundation 19 | 20 | @testable import KituraWebSocket 21 | import KituraNet 22 | 23 | class TestWebSocketService: WebSocketService { 24 | let closeReason: WebSocketCloseReasonCode 25 | let pingMessage: String? 26 | let testServerRequest: Bool 27 | var connectionTimeout: Int? 28 | var connections: [String: WebSocketConnection] 29 | 30 | public init(closeReason: WebSocketCloseReasonCode, testServerRequest: Bool, pingMessage: String?, connectionTimeout: Int? = nil) { 31 | self.closeReason = closeReason 32 | self.testServerRequest = testServerRequest 33 | self.pingMessage = pingMessage 34 | self.connectionTimeout = connectionTimeout 35 | self.connections = [:] 36 | } 37 | 38 | public func connected(connection: WebSocketConnection) { 39 | connections[connection.id] = connection 40 | 41 | if let pingMessage = pingMessage { 42 | if pingMessage.count > 0 { 43 | connection.ping(withMessage: pingMessage) 44 | } 45 | else { 46 | connection.ping() 47 | } 48 | } 49 | 50 | if testServerRequest { 51 | performServerRequestTests(request: connection.request) 52 | 53 | sleep(2) 54 | 55 | performServerRequestTests(request: connection.request) 56 | } 57 | } 58 | 59 | private func performServerRequestTests(request: ServerRequest) { 60 | XCTAssertEqual(request.method, "GET", "The method of the request should be GET, it was \(request.method))") 61 | XCTAssertEqual(request.httpVersionMajor, 1, "HTTP version major should be 1, it was \(String(describing: request.httpVersionMajor))") 62 | XCTAssertEqual(request.httpVersionMinor, 1, "HTTP version major should be 1, it was \(String(describing: request.httpVersionMinor))") 63 | XCTAssertEqual(request.urlURL.pathComponents[1], "wstester", "Path of the request should be /wstester, it was /\(request.urlURL.pathComponents[1])") 64 | XCTAssertEqual(request.url, "/wstester".data(using: .utf8)!, "Path of the request should be /wstester, it was \(String(data: request.url, encoding: .utf8) ?? "Not UTF-8")") 65 | let protocolVersion = request.headers["Sec-WebSocket-Version"] 66 | XCTAssertNotNil(protocolVersion, "The Sec-WebSocket-Version header wasn't in the headers") 67 | XCTAssertEqual(protocolVersion!.count, 1, "The Sec-WebSocket-Version header should have one value") 68 | XCTAssertEqual(protocolVersion![0], "13", "The Sec-WebSocket-Version header value should be 13, it was \(protocolVersion![0])") 69 | 70 | do { 71 | let bodyString = try request.readString() 72 | XCTAssertNil(bodyString, "Read of body should have returned nil, it returned \"\(String(describing: bodyString))\"") 73 | var body = Data() 74 | var count = try request.read(into: &body) 75 | XCTAssertEqual(count, 0, "Read of body into a Data should have returned 0, it returned \(count)") 76 | count = try request.readAllData(into: &body) 77 | XCTAssertEqual(count, 0, "Read of entire body into a Data should have returned 0, it returned \(count)") 78 | } 79 | catch { 80 | XCTFail("Failed to read from the body. Error=\(error)") 81 | } 82 | } 83 | 84 | public func disconnected(connection: WebSocketConnection, reason: WebSocketCloseReasonCode) { 85 | XCTAssertNotNil(connections[connection.id], "Client ID from connect wasn't client ID from disconnect") 86 | XCTAssert((closeReason.code() == reason.code()), "Excpected close reason code of \(closeReason) received \(reason)") 87 | connections[connection.id] = nil 88 | } 89 | 90 | public func received(message: Data, from: WebSocketConnection) { 91 | print("Received a binary message of length \(message.count)") 92 | from.send(message: message) 93 | } 94 | 95 | public func received(message: String, from: WebSocketConnection) { 96 | print("Received a String message of length \(message.count)") 97 | from.send(message: message) 98 | 99 | if message == "close" { 100 | from.close(reason: .goingAway, description: "Going away...") 101 | } 102 | else if message == "drop" { 103 | from.drop(reason: .policyViolation, description: "Droping...") 104 | } 105 | else if message == "ping" { 106 | from.ping(withMessage: "Hello") 107 | } 108 | } 109 | } 110 | 111 | extension TestWebSocketService: CustomStringConvertible { 112 | /// Generate a printable version of this enum. 113 | public var description: String { 114 | return "TestWebSocketService(closeReason: \(closeReason))" 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Tests/KituraWebSocketTests/UpgradeErrors.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2016 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | import XCTest 18 | import Foundation 19 | 20 | @testable import KituraWebSocket 21 | @testable import KituraNet 22 | import Socket 23 | 24 | class UpgradeErrors: KituraTest { 25 | 26 | static var allTests: [(String, (UpgradeErrors) -> () throws -> Void)] { 27 | return [ 28 | ("testNoSecWebSocketKey", testNoSecWebSocketKey), 29 | ("testNoSecWebSocketVersion", testNoSecWebSocketVersion), 30 | ("testNoService", testNoService) 31 | ] 32 | } 33 | 34 | func testNoSecWebSocketKey() { 35 | WebSocket.factory.clear() 36 | 37 | performServerTest() { expectation in 38 | guard let socket = self.sendUpgradeRequest(forProtocolVersion: "13", toPath: "/testing123", usingKey: nil) else { return } 39 | 40 | self.checkUpgradeFailureResponse(from: socket, expectedMessage: "Sec-WebSocket-Key header missing in the upgrade request", expectation: expectation) 41 | } 42 | } 43 | 44 | func testNoSecWebSocketVersion() { 45 | WebSocket.factory.clear() 46 | 47 | performServerTest(asyncTasks: { expectation in 48 | guard let socket = self.sendUpgradeRequest(forProtocolVersion: nil, toPath: "/testing123", usingKey: nil) else { return } 49 | 50 | self.checkUpgradeFailureResponse(from: socket, expectedMessage: "Sec-WebSocket-Version header missing in the upgrade request", expectation: expectation) 51 | }, 52 | { expectation in 53 | guard let socket = self.sendUpgradeRequest(forProtocolVersion: "12", toPath: "/testing123", usingKey: nil) else { return } 54 | 55 | self.checkUpgradeFailureResponse(from: socket, expectedMessage: "Only WebSocket protocol version 13 is supported", expectation: expectation) 56 | }) 57 | } 58 | 59 | func testNoService() { 60 | WebSocket.factory.clear() 61 | 62 | performServerTest() { expectation in 63 | guard let socket = self.sendUpgradeRequest(forProtocolVersion: "13", toPath: "/testing123", usingKey: "test") else { return } 64 | 65 | self.checkUpgradeFailureResponse(from: socket, expectedMessage: "No service has been registered for the path /testing123", expectation: expectation) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2016 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | import XCTest 18 | import Glibc 19 | @testable import KituraWebSocketTests 20 | 21 | // http://stackoverflow.com/questions/24026510/how-do-i-shuffle-an-array-in-swift 22 | #if swift(>=3.2) 23 | extension MutableCollection { 24 | mutating func shuffle() { 25 | let c = count 26 | guard c > 1 else { return } 27 | 28 | srand(UInt32(time(nil))) 29 | for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) { 30 | #if swift(>=4.1) 31 | let d: Int = numericCast(random() % numericCast(unshuffledCount)) 32 | #else 33 | let d: IndexDistance = numericCast(random() % numericCast(unshuffledCount)) 34 | #endif 35 | guard d != 0 else { continue } 36 | let i = index(firstUnshuffled, offsetBy: d) 37 | swapAt(firstUnshuffled, i) 38 | } 39 | } 40 | } 41 | #else 42 | extension MutableCollection where Indices.Iterator.Element == Index { 43 | mutating func shuffle() { 44 | let c = count 45 | guard c > 1 else { return } 46 | 47 | srand(UInt32(time(nil))) 48 | for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) { 49 | let d: IndexDistance = numericCast(random() % numericCast(unshuffledCount)) 50 | guard d != 0 else { continue } 51 | let i = index(firstUnshuffled, offsetBy: d) 52 | swap(&self[firstUnshuffled], &self[i]) 53 | } 54 | } 55 | } 56 | #endif 57 | 58 | extension Sequence { 59 | func shuffled() -> [Iterator.Element] { 60 | var result = Array(self) 61 | result.shuffle() 62 | return result 63 | } 64 | } 65 | 66 | XCTMain([ 67 | testCase(BasicTests.allTests.shuffled()), 68 | testCase(ComplexTests.allTests.shuffled()), 69 | testCase(ConnectionCleanupTests.allTests.shuffled()), 70 | testCase(ProtocolErrorTests.allTests.shuffled()), 71 | testCase(UpgradeErrors.allTests.shuffled()) 72 | ].shuffled()) 73 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | KituraWebSocket Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

Classes

93 |

The following classes are available globally.

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | WSConnectionUpgradeFactory 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    The implementation of the ConnectionUpgradeFactory protocol for the WebSocket protocol. 116 | Participates in the HTTP protocol upgrade process when upgarding to the WebSocket protocol.

    117 | 118 | See more 119 |
    120 |
    121 |

    Declaration

    122 |
    123 |

    Swift

    124 |
    public class WSConnectionUpgradeFactory : ConnectionUpgradeFactory
    125 | 126 |
    127 |
    128 |
    129 |
    130 |
  • 131 |
  • 132 |
    133 | 134 | 135 | 136 | WebSocket 137 | 138 |
    139 |
    140 |
    141 |
    142 |
    143 |
    144 |

    Main class for the Kitura-WebSocket API. Used to register WebSocketService classes 145 | that will handle WebSocket connections for specific paths.

    146 | 147 | See more 148 |
    149 |
    150 |

    Declaration

    151 |
    152 |

    Swift

    153 |
    public class WebSocket
    154 | 155 |
    156 |
    157 |
    158 |
    159 |
  • 160 |
  • 161 |
    162 | 163 | 164 | 165 | WebSocketConnection 166 | 167 |
    168 |
    169 |
    170 |
    171 |
    172 |
    173 |

    Represents a specific WebSocket connection. Provides a unique identifier for 174 | the connection and APIs to send messages and control commands to the client 175 | at the other end of the connection.

    176 | 177 | See more 178 |
    179 |
    180 |

    Declaration

    181 |
    182 |

    Swift

    183 |
    public class WebSocketConnection
    184 | 185 |
    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | KituraWebSocket Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

Enumerations

93 |

The following enumerations are available globally.

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | WebSocketCloseReasonCode 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    The WebSocketCloseReasonCode enum defines the set of reason codes that a 116 | WebSocket application can send/receive when a connection is closed.

    117 | 118 | See more 119 |
    120 |
    121 |

    Declaration

    122 |
    123 |

    Swift

    124 |
    public enum WebSocketCloseReasonCode
    125 | 126 |
    127 |
    128 |
    129 |
    130 |
  • 131 |
  • 132 |
    133 | 134 | 135 | 136 | WebSocketError 137 | 138 |
    139 |
    140 |
    141 |
    142 |
    143 |
    144 |

    An error enum used when throwing errors within KituraWebSocket.

    145 | 146 | See more 147 |
    148 |
    149 |

    Declaration

    150 |
    151 |

    Swift

    152 |
    public enum WebSocketError : Error
    153 |
    extension WebSocketError: CustomStringConvertible
    154 | 155 |
    156 |
    157 |
    158 |
    159 |
  • 160 |
161 |
162 |
163 |
164 | 165 |
166 |
167 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /docs/Enums/WebSocketError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WebSocketError Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | KituraWebSocket Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

WebSocketError

93 |
94 |
95 | 96 |
public enum WebSocketError : Error
97 |
extension WebSocketError: CustomStringConvertible
98 | 99 |
100 |
101 |

An error enum used when throwing errors within KituraWebSocket.

102 | 103 |
104 |
105 | 106 |
107 |
108 |
109 |
    110 |
  • 111 |
    112 | 113 | 114 | 115 | invalidOpCode(_:) 116 | 117 |
    118 |
    119 |
    120 |
    121 |
    122 |
    123 |

    An invalid opcode was received in a WebSocket frame

    124 | 125 |
    126 |
    127 |

    Declaration

    128 |
    129 |

    Swift

    130 |
    case invalidOpCode(UInt8)
    131 | 132 |
    133 |
    134 |
    135 |
    136 |
  • 137 |
  • 138 |
    139 | 140 | 141 | 142 | unmaskedFrame 143 | 144 |
    145 |
    146 |
    147 |
    148 |
    149 |
    150 |

    A frame was received that had an unmasked payload

    151 | 152 |
    153 |
    154 |

    Declaration

    155 |
    156 |

    Swift

    157 |
    case unmaskedFrame
    158 | 159 |
    160 |
    161 |
    162 |
    163 |
  • 164 |
  • 165 |
    166 | 167 | 168 | 169 | description 170 | 171 |
    172 |
    173 |
    174 |
    175 |
    176 |
    177 |

    Generate a printable version of this enum.

    178 | 179 |
    180 |
    181 |

    Declaration

    182 |
    183 |

    Swift

    184 |
    public var description: String { get }
    185 | 186 |
    187 |
    188 |
    189 |
    190 |
  • 191 |
192 |
193 |
194 |
195 | 196 |
197 |
198 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /docs/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | KituraWebSocket Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

Protocols

93 |

The following protocols are available globally.

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | WebSocketService 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    The WebSocketService protocol is implemented by classes that wish to be WebSocket server side 116 | end points. An instance of the WebSocketService protocol is the server side of a WebSocket connection. 117 | There can be many WebSocket connections connected to a single WebSocketService protocol instance. 118 | The protocol is a set of callbacks that are invoked when various events occur.

    119 | 120 | See more 121 |
    122 |
    123 |

    Declaration

    124 |
    125 |

    Swift

    126 |
    public protocol WebSocketService : AnyObject
    127 | 128 |
    129 |
    130 |
    131 |
    132 |
  • 133 |
134 |
135 |
136 |
137 | 138 |
139 |
140 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 100% 23 | 24 | 25 | 100% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #4183c4; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | a.discouraged { 67 | text-decoration: line-through; } 68 | a.discouraged:hover, a.discouraged:focus { 69 | text-decoration: underline line-through; } 70 | 71 | table { 72 | background: #fff; 73 | width: 100%; 74 | border-collapse: collapse; 75 | border-spacing: 0; 76 | overflow: auto; 77 | margin: 0 0 0.85em; } 78 | 79 | tr:nth-child(2n) { 80 | background-color: #fbfbfb; } 81 | 82 | th, td { 83 | padding: 6px 13px; 84 | border: 1px solid #ddd; } 85 | 86 | pre { 87 | margin: 0 0 1.275em; 88 | padding: .85em 1em; 89 | overflow: auto; 90 | background: #f7f7f7; 91 | font-size: .85em; 92 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 93 | 94 | code { 95 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 96 | 97 | .item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { 98 | background: #f7f7f7; 99 | padding: .2em; } 100 | .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { 101 | letter-spacing: -.2em; 102 | content: "\00a0"; } 103 | 104 | pre code { 105 | padding: 0; 106 | white-space: pre; } 107 | 108 | .content-wrapper { 109 | display: flex; 110 | flex-direction: column; } 111 | @media (min-width: 768px) { 112 | .content-wrapper { 113 | flex-direction: row; } } 114 | .header { 115 | display: flex; 116 | padding: 8px; 117 | font-size: 0.875em; 118 | background: #444; 119 | color: #999; } 120 | 121 | .header-col { 122 | margin: 0; 123 | padding: 0 8px; } 124 | 125 | .header-col--primary { 126 | flex: 1; } 127 | 128 | .header-link { 129 | color: #fff; } 130 | 131 | .header-icon { 132 | padding-right: 6px; 133 | vertical-align: -4px; 134 | height: 16px; } 135 | 136 | .breadcrumbs { 137 | font-size: 0.875em; 138 | padding: 8px 16px; 139 | margin: 0; 140 | background: #fbfbfb; 141 | border-bottom: 1px solid #ddd; } 142 | 143 | .carat { 144 | height: 10px; 145 | margin: 0 5px; } 146 | 147 | .navigation { 148 | order: 2; } 149 | @media (min-width: 768px) { 150 | .navigation { 151 | order: 1; 152 | width: 25%; 153 | max-width: 300px; 154 | padding-bottom: 64px; 155 | overflow: hidden; 156 | word-wrap: normal; 157 | background: #fbfbfb; 158 | border-right: 1px solid #ddd; } } 159 | .nav-groups { 160 | list-style-type: none; 161 | padding-left: 0; } 162 | 163 | .nav-group-name { 164 | border-bottom: 1px solid #ddd; 165 | padding: 8px 0 8px 16px; } 166 | 167 | .nav-group-name-link { 168 | color: #333; } 169 | 170 | .nav-group-tasks { 171 | margin: 8px 0; 172 | padding: 0 0 0 8px; } 173 | 174 | .nav-group-task { 175 | font-size: 1em; 176 | list-style-type: none; 177 | white-space: nowrap; } 178 | 179 | .nav-group-task-link { 180 | color: #808080; } 181 | 182 | .main-content { 183 | order: 1; } 184 | @media (min-width: 768px) { 185 | .main-content { 186 | order: 2; 187 | flex: 1; 188 | padding-bottom: 60px; } } 189 | .section { 190 | padding: 0 32px; 191 | border-bottom: 1px solid #ddd; } 192 | 193 | .section-content { 194 | max-width: 834px; 195 | margin: 0 auto; 196 | padding: 16px 0; } 197 | 198 | .section-name { 199 | color: #666; 200 | display: block; } 201 | .section-name p { 202 | margin-bottom: inherit; } 203 | 204 | .declaration .highlight { 205 | overflow-x: initial; 206 | padding: 8px 0; 207 | margin: 0; 208 | background-color: transparent; 209 | border: none; } 210 | 211 | .task-group-section { 212 | border-top: 1px solid #ddd; } 213 | 214 | .task-group { 215 | padding-top: 0px; } 216 | 217 | .task-name-container a[name]:before { 218 | content: ""; 219 | display: block; } 220 | 221 | .section-name-container { 222 | position: relative; } 223 | .section-name-container .section-name-link { 224 | position: absolute; 225 | top: 0; 226 | left: 0; 227 | bottom: 0; 228 | right: 0; 229 | margin-bottom: 0; } 230 | .section-name-container .section-name { 231 | position: relative; 232 | pointer-events: none; 233 | z-index: 1; } 234 | .section-name-container .section-name a { 235 | pointer-events: auto; } 236 | 237 | .item-container { 238 | padding: 0; } 239 | 240 | .item { 241 | padding-top: 8px; 242 | width: 100%; 243 | list-style-type: none; } 244 | .item a[name]:before { 245 | content: ""; 246 | display: block; } 247 | .item .token, .item .direct-link { 248 | display: inline-block; 249 | text-indent: -20px; 250 | padding-left: 3px; 251 | margin-left: 20px; 252 | font-size: 1rem; } 253 | .item .declaration-note { 254 | font-size: .85em; 255 | color: #808080; 256 | font-style: italic; } 257 | 258 | .pointer-container { 259 | border-bottom: 1px solid #ddd; 260 | left: -23px; 261 | padding-bottom: 13px; 262 | position: relative; 263 | width: 110%; } 264 | 265 | .pointer { 266 | left: 21px; 267 | top: 7px; 268 | display: block; 269 | position: absolute; 270 | width: 12px; 271 | height: 12px; 272 | border-left: 1px solid #ddd; 273 | border-top: 1px solid #ddd; 274 | background: #fff; 275 | transform: rotate(45deg); } 276 | 277 | .height-container { 278 | display: none; 279 | position: relative; 280 | width: 100%; 281 | overflow: hidden; } 282 | .height-container .section { 283 | background: #fff; 284 | border: 1px solid #ddd; 285 | border-top-width: 0; 286 | padding-top: 10px; 287 | padding-bottom: 5px; 288 | padding: 8px 16px; } 289 | 290 | .aside, .language { 291 | padding: 6px 12px; 292 | margin: 12px 0; 293 | border-left: 5px solid #dddddd; 294 | overflow-y: hidden; } 295 | .aside .aside-title, .language .aside-title { 296 | font-size: 9px; 297 | letter-spacing: 2px; 298 | text-transform: uppercase; 299 | padding-bottom: 0; 300 | margin: 0; 301 | color: #aaa; 302 | -webkit-user-select: none; } 303 | .aside p:last-child, .language p:last-child { 304 | margin-bottom: 0; } 305 | 306 | .language { 307 | border-left: 5px solid #cde9f4; } 308 | .language .aside-title { 309 | color: #4183c4; } 310 | 311 | .aside-warning, .aside-deprecated, .aside-unavailable { 312 | border-left: 5px solid #ff6666; } 313 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 314 | color: #ff0000; } 315 | 316 | .graybox { 317 | border-collapse: collapse; 318 | width: 100%; } 319 | .graybox p { 320 | margin: 0; 321 | word-break: break-word; 322 | min-width: 50px; } 323 | .graybox td { 324 | border: 1px solid #ddd; 325 | padding: 5px 25px 5px 10px; 326 | vertical-align: middle; } 327 | .graybox tr td:first-of-type { 328 | text-align: right; 329 | padding: 7px; 330 | vertical-align: top; 331 | word-break: normal; 332 | width: 40px; } 333 | 334 | .slightly-smaller { 335 | font-size: 0.9em; } 336 | 337 | .footer { 338 | padding: 8px 16px; 339 | background: #444; 340 | color: #ddd; 341 | font-size: 0.8em; } 342 | .footer p { 343 | margin: 8px 0; } 344 | .footer a { 345 | color: #fff; } 346 | 347 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 348 | display: none; } 349 | 350 | html.dash .height-container { 351 | display: block; } 352 | 353 | form[role=search] input { 354 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 355 | font-size: 14px; 356 | line-height: 24px; 357 | padding: 0 10px; 358 | margin: 0; 359 | border: none; 360 | border-radius: 1em; } 361 | .loading form[role=search] input { 362 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 363 | 364 | form[role=search] .tt-menu { 365 | margin: 0; 366 | min-width: 300px; 367 | background: #fbfbfb; 368 | color: #333; 369 | border: 1px solid #ddd; } 370 | 371 | form[role=search] .tt-highlight { 372 | font-weight: bold; } 373 | 374 | form[role=search] .tt-suggestion { 375 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 376 | padding: 0 8px; } 377 | form[role=search] .tt-suggestion span { 378 | display: table-cell; 379 | white-space: nowrap; } 380 | form[role=search] .tt-suggestion .doc-parent-name { 381 | width: 100%; 382 | text-align: right; 383 | font-weight: normal; 384 | font-size: 0.9em; 385 | padding-left: 16px; } 386 | 387 | form[role=search] .tt-suggestion:hover, 388 | form[role=search] .tt-suggestion.tt-cursor { 389 | cursor: pointer; 390 | background-color: #4183c4; 391 | color: #fff; } 392 | 393 | form[role=search] .tt-suggestion:hover .doc-parent-name, 394 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 395 | color: #fff; } 396 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.kiturawebsocket 7 | CFBundleName 8 | KituraWebSocket 9 | DocSetPlatformFamily 10 | kiturawebsocket 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | KituraWebSocket Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

Classes

93 |

The following classes are available globally.

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | WSConnectionUpgradeFactory 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    The implementation of the ConnectionUpgradeFactory protocol for the WebSocket protocol. 116 | Participates in the HTTP protocol upgrade process when upgarding to the WebSocket protocol.

    117 | 118 | See more 119 |
    120 |
    121 |

    Declaration

    122 |
    123 |

    Swift

    124 |
    public class WSConnectionUpgradeFactory : ConnectionUpgradeFactory
    125 | 126 |
    127 |
    128 |
    129 |
    130 |
  • 131 |
  • 132 |
    133 | 134 | 135 | 136 | WebSocket 137 | 138 |
    139 |
    140 |
    141 |
    142 |
    143 |
    144 |

    Main class for the Kitura-WebSocket API. Used to register WebSocketService classes 145 | that will handle WebSocket connections for specific paths.

    146 | 147 | See more 148 |
    149 |
    150 |

    Declaration

    151 |
    152 |

    Swift

    153 |
    public class WebSocket
    154 | 155 |
    156 |
    157 |
    158 |
    159 |
  • 160 |
  • 161 |
    162 | 163 | 164 | 165 | WebSocketConnection 166 | 167 |
    168 |
    169 |
    170 |
    171 |
    172 |
    173 |

    Represents a specific WebSocket connection. Provides a unique identifier for 174 | the connection and APIs to send messages and control commands to the client 175 | at the other end of the connection.

    176 | 177 | See more 178 |
    179 |
    180 |

    Declaration

    181 |
    182 |

    Swift

    183 |
    public class WebSocketConnection
    184 | 185 |
    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | KituraWebSocket Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

Enumerations

93 |

The following enumerations are available globally.

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | WebSocketCloseReasonCode 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    The WebSocketCloseReasonCode enum defines the set of reason codes that a 116 | WebSocket application can send/receive when a connection is closed.

    117 | 118 | See more 119 |
    120 |
    121 |

    Declaration

    122 |
    123 |

    Swift

    124 |
    public enum WebSocketCloseReasonCode
    125 | 126 |
    127 |
    128 |
    129 |
    130 |
  • 131 |
  • 132 |
    133 | 134 | 135 | 136 | WebSocketError 137 | 138 |
    139 |
    140 |
    141 |
    142 |
    143 |
    144 |

    An error enum used when throwing errors within KituraWebSocket.

    145 | 146 | See more 147 |
    148 |
    149 |

    Declaration

    150 |
    151 |

    Swift

    152 |
    public enum WebSocketError : Error
    153 |
    extension WebSocketError: CustomStringConvertible
    154 | 155 |
    156 |
    157 |
    158 |
    159 |
  • 160 |
161 |
162 |
163 |
164 | 165 |
166 |
167 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/Enums/WebSocketError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WebSocketError Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | KituraWebSocket Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

WebSocketError

93 |
94 |
95 | 96 |
public enum WebSocketError : Error
97 |
extension WebSocketError: CustomStringConvertible
98 | 99 |
100 |
101 |

An error enum used when throwing errors within KituraWebSocket.

102 | 103 |
104 |
105 | 106 |
107 |
108 |
109 |
    110 |
  • 111 |
    112 | 113 | 114 | 115 | invalidOpCode(_:) 116 | 117 |
    118 |
    119 |
    120 |
    121 |
    122 |
    123 |

    An invalid opcode was received in a WebSocket frame

    124 | 125 |
    126 |
    127 |

    Declaration

    128 |
    129 |

    Swift

    130 |
    case invalidOpCode(UInt8)
    131 | 132 |
    133 |
    134 |
    135 |
    136 |
  • 137 |
  • 138 |
    139 | 140 | 141 | 142 | unmaskedFrame 143 | 144 |
    145 |
    146 |
    147 |
    148 |
    149 |
    150 |

    A frame was received that had an unmasked payload

    151 | 152 |
    153 |
    154 |

    Declaration

    155 |
    156 |

    Swift

    157 |
    case unmaskedFrame
    158 | 159 |
    160 |
    161 |
    162 |
    163 |
  • 164 |
  • 165 |
    166 | 167 | 168 | 169 | description 170 | 171 |
    172 |
    173 |
    174 |
    175 |
    176 |
    177 |

    Generate a printable version of this enum.

    178 | 179 |
    180 |
    181 |

    Declaration

    182 |
    183 |

    Swift

    184 |
    public var description: String { get }
    185 | 186 |
    187 |
    188 |
    189 |
    190 |
  • 191 |
192 |
193 |
194 |
195 | 196 |
197 |
198 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | KituraWebSocket Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

Protocols

93 |

The following protocols are available globally.

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | WebSocketService 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    The WebSocketService protocol is implemented by classes that wish to be WebSocket server side 116 | end points. An instance of the WebSocketService protocol is the server side of a WebSocket connection. 117 | There can be many WebSocket connections connected to a single WebSocketService protocol instance. 118 | The protocol is a set of callbacks that are invoked when various events occur.

    119 | 120 | See more 121 |
    122 |
    123 |

    Declaration

    124 |
    125 |

    Swift

    126 |
    public protocol WebSocketService : AnyObject
    127 | 128 |
    129 |
    130 |
    131 |
    132 |
  • 133 |
134 |
135 |
136 |
137 | 138 |
139 |
140 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #4183c4; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | a.discouraged { 67 | text-decoration: line-through; } 68 | a.discouraged:hover, a.discouraged:focus { 69 | text-decoration: underline line-through; } 70 | 71 | table { 72 | background: #fff; 73 | width: 100%; 74 | border-collapse: collapse; 75 | border-spacing: 0; 76 | overflow: auto; 77 | margin: 0 0 0.85em; } 78 | 79 | tr:nth-child(2n) { 80 | background-color: #fbfbfb; } 81 | 82 | th, td { 83 | padding: 6px 13px; 84 | border: 1px solid #ddd; } 85 | 86 | pre { 87 | margin: 0 0 1.275em; 88 | padding: .85em 1em; 89 | overflow: auto; 90 | background: #f7f7f7; 91 | font-size: .85em; 92 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 93 | 94 | code { 95 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 96 | 97 | .item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { 98 | background: #f7f7f7; 99 | padding: .2em; } 100 | .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { 101 | letter-spacing: -.2em; 102 | content: "\00a0"; } 103 | 104 | pre code { 105 | padding: 0; 106 | white-space: pre; } 107 | 108 | .content-wrapper { 109 | display: flex; 110 | flex-direction: column; } 111 | @media (min-width: 768px) { 112 | .content-wrapper { 113 | flex-direction: row; } } 114 | .header { 115 | display: flex; 116 | padding: 8px; 117 | font-size: 0.875em; 118 | background: #444; 119 | color: #999; } 120 | 121 | .header-col { 122 | margin: 0; 123 | padding: 0 8px; } 124 | 125 | .header-col--primary { 126 | flex: 1; } 127 | 128 | .header-link { 129 | color: #fff; } 130 | 131 | .header-icon { 132 | padding-right: 6px; 133 | vertical-align: -4px; 134 | height: 16px; } 135 | 136 | .breadcrumbs { 137 | font-size: 0.875em; 138 | padding: 8px 16px; 139 | margin: 0; 140 | background: #fbfbfb; 141 | border-bottom: 1px solid #ddd; } 142 | 143 | .carat { 144 | height: 10px; 145 | margin: 0 5px; } 146 | 147 | .navigation { 148 | order: 2; } 149 | @media (min-width: 768px) { 150 | .navigation { 151 | order: 1; 152 | width: 25%; 153 | max-width: 300px; 154 | padding-bottom: 64px; 155 | overflow: hidden; 156 | word-wrap: normal; 157 | background: #fbfbfb; 158 | border-right: 1px solid #ddd; } } 159 | .nav-groups { 160 | list-style-type: none; 161 | padding-left: 0; } 162 | 163 | .nav-group-name { 164 | border-bottom: 1px solid #ddd; 165 | padding: 8px 0 8px 16px; } 166 | 167 | .nav-group-name-link { 168 | color: #333; } 169 | 170 | .nav-group-tasks { 171 | margin: 8px 0; 172 | padding: 0 0 0 8px; } 173 | 174 | .nav-group-task { 175 | font-size: 1em; 176 | list-style-type: none; 177 | white-space: nowrap; } 178 | 179 | .nav-group-task-link { 180 | color: #808080; } 181 | 182 | .main-content { 183 | order: 1; } 184 | @media (min-width: 768px) { 185 | .main-content { 186 | order: 2; 187 | flex: 1; 188 | padding-bottom: 60px; } } 189 | .section { 190 | padding: 0 32px; 191 | border-bottom: 1px solid #ddd; } 192 | 193 | .section-content { 194 | max-width: 834px; 195 | margin: 0 auto; 196 | padding: 16px 0; } 197 | 198 | .section-name { 199 | color: #666; 200 | display: block; } 201 | .section-name p { 202 | margin-bottom: inherit; } 203 | 204 | .declaration .highlight { 205 | overflow-x: initial; 206 | padding: 8px 0; 207 | margin: 0; 208 | background-color: transparent; 209 | border: none; } 210 | 211 | .task-group-section { 212 | border-top: 1px solid #ddd; } 213 | 214 | .task-group { 215 | padding-top: 0px; } 216 | 217 | .task-name-container a[name]:before { 218 | content: ""; 219 | display: block; } 220 | 221 | .section-name-container { 222 | position: relative; } 223 | .section-name-container .section-name-link { 224 | position: absolute; 225 | top: 0; 226 | left: 0; 227 | bottom: 0; 228 | right: 0; 229 | margin-bottom: 0; } 230 | .section-name-container .section-name { 231 | position: relative; 232 | pointer-events: none; 233 | z-index: 1; } 234 | .section-name-container .section-name a { 235 | pointer-events: auto; } 236 | 237 | .item-container { 238 | padding: 0; } 239 | 240 | .item { 241 | padding-top: 8px; 242 | width: 100%; 243 | list-style-type: none; } 244 | .item a[name]:before { 245 | content: ""; 246 | display: block; } 247 | .item .token, .item .direct-link { 248 | display: inline-block; 249 | text-indent: -20px; 250 | padding-left: 3px; 251 | margin-left: 20px; 252 | font-size: 1rem; } 253 | .item .declaration-note { 254 | font-size: .85em; 255 | color: #808080; 256 | font-style: italic; } 257 | 258 | .pointer-container { 259 | border-bottom: 1px solid #ddd; 260 | left: -23px; 261 | padding-bottom: 13px; 262 | position: relative; 263 | width: 110%; } 264 | 265 | .pointer { 266 | left: 21px; 267 | top: 7px; 268 | display: block; 269 | position: absolute; 270 | width: 12px; 271 | height: 12px; 272 | border-left: 1px solid #ddd; 273 | border-top: 1px solid #ddd; 274 | background: #fff; 275 | transform: rotate(45deg); } 276 | 277 | .height-container { 278 | display: none; 279 | position: relative; 280 | width: 100%; 281 | overflow: hidden; } 282 | .height-container .section { 283 | background: #fff; 284 | border: 1px solid #ddd; 285 | border-top-width: 0; 286 | padding-top: 10px; 287 | padding-bottom: 5px; 288 | padding: 8px 16px; } 289 | 290 | .aside, .language { 291 | padding: 6px 12px; 292 | margin: 12px 0; 293 | border-left: 5px solid #dddddd; 294 | overflow-y: hidden; } 295 | .aside .aside-title, .language .aside-title { 296 | font-size: 9px; 297 | letter-spacing: 2px; 298 | text-transform: uppercase; 299 | padding-bottom: 0; 300 | margin: 0; 301 | color: #aaa; 302 | -webkit-user-select: none; } 303 | .aside p:last-child, .language p:last-child { 304 | margin-bottom: 0; } 305 | 306 | .language { 307 | border-left: 5px solid #cde9f4; } 308 | .language .aside-title { 309 | color: #4183c4; } 310 | 311 | .aside-warning, .aside-deprecated, .aside-unavailable { 312 | border-left: 5px solid #ff6666; } 313 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 314 | color: #ff0000; } 315 | 316 | .graybox { 317 | border-collapse: collapse; 318 | width: 100%; } 319 | .graybox p { 320 | margin: 0; 321 | word-break: break-word; 322 | min-width: 50px; } 323 | .graybox td { 324 | border: 1px solid #ddd; 325 | padding: 5px 25px 5px 10px; 326 | vertical-align: middle; } 327 | .graybox tr td:first-of-type { 328 | text-align: right; 329 | padding: 7px; 330 | vertical-align: top; 331 | word-break: normal; 332 | width: 40px; } 333 | 334 | .slightly-smaller { 335 | font-size: 0.9em; } 336 | 337 | .footer { 338 | padding: 8px 16px; 339 | background: #444; 340 | color: #ddd; 341 | font-size: 0.8em; } 342 | .footer p { 343 | margin: 8px 0; } 344 | .footer a { 345 | color: #fff; } 346 | 347 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 348 | display: none; } 349 | 350 | html.dash .height-container { 351 | display: block; } 352 | 353 | form[role=search] input { 354 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 355 | font-size: 14px; 356 | line-height: 24px; 357 | padding: 0 10px; 358 | margin: 0; 359 | border: none; 360 | border-radius: 1em; } 361 | .loading form[role=search] input { 362 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 363 | 364 | form[role=search] .tt-menu { 365 | margin: 0; 366 | min-width: 300px; 367 | background: #fbfbfb; 368 | color: #333; 369 | border: 1px solid #ddd; } 370 | 371 | form[role=search] .tt-highlight { 372 | font-weight: bold; } 373 | 374 | form[role=search] .tt-suggestion { 375 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 376 | padding: 0 8px; } 377 | form[role=search] .tt-suggestion span { 378 | display: table-cell; 379 | white-space: nowrap; } 380 | form[role=search] .tt-suggestion .doc-parent-name { 381 | width: 100%; 382 | text-align: right; 383 | font-weight: normal; 384 | font-size: 0.9em; 385 | padding-left: 16px; } 386 | 387 | form[role=search] .tt-suggestion:hover, 388 | form[role=search] .tt-suggestion.tt-cursor { 389 | cursor: pointer; 390 | background-color: #4183c4; 391 | color: #fff; } 392 | 393 | form[role=search] .tt-suggestion:hover .doc-parent-name, 394 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 395 | color: #fff; } 396 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/img/spinner.gif -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | 61 | // KaTeX rendering 62 | if ("katex" in window) { 63 | $($('.math').each( (_, element) => { 64 | katex.render(element.textContent, element, { 65 | displayMode: $(element).hasClass('m-block'), 66 | throwOnError: false, 67 | trust: true 68 | }); 69 | })) 70 | } 71 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var $typeahead = $('[data-typeahead]'); 3 | var $form = $typeahead.parents('form'); 4 | var searchURL = $form.attr('action'); 5 | 6 | function displayTemplate(result) { 7 | return result.name; 8 | } 9 | 10 | function suggestionTemplate(result) { 11 | var t = '
'; 12 | t += '' + result.name + ''; 13 | if (result.parent_name) { 14 | t += '' + result.parent_name + ''; 15 | } 16 | t += '
'; 17 | return t; 18 | } 19 | 20 | $typeahead.one('focus', function() { 21 | $form.addClass('loading'); 22 | 23 | $.getJSON(searchURL).then(function(searchData) { 24 | const searchIndex = lunr(function() { 25 | this.ref('url'); 26 | this.field('name'); 27 | this.field('abstract'); 28 | for (const [url, doc] of Object.entries(searchData)) { 29 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 30 | } 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3, 37 | autoselect: true 38 | }, 39 | { 40 | limit: 10, 41 | display: displayTemplate, 42 | templates: { suggestion: suggestionTemplate }, 43 | source: function(query, sync) { 44 | const lcSearch = query.toLowerCase(); 45 | const results = searchIndex.query(function(q) { 46 | q.term(lcSearch, { boost: 100 }); 47 | q.term(lcSearch, { 48 | boost: 10, 49 | wildcard: lunr.Query.wildcard.TRAILING 50 | }); 51 | }).map(function(result) { 52 | var doc = searchData[result.ref]; 53 | doc.url = result.ref; 54 | return doc; 55 | }); 56 | sync(results); 57 | } 58 | } 59 | ); 60 | $form.removeClass('loading'); 61 | $typeahead.trigger('focus'); 62 | }); 63 | }); 64 | 65 | var baseURL = searchURL.slice(0, -"search.json".length); 66 | 67 | $typeahead.on('typeahead:select', function(e, result) { 68 | window.location = baseURL + result.url; 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/Documents/search.json: -------------------------------------------------------------------------------- 1 | {"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP9connected10connectionyAA0bC10ConnectionC_tF":{"name":"connected(connection:)","abstract":"

Called when a WebSocket client connects to the server and is connected to a specific","parent_name":"WebSocketService"},"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP12disconnected10connection6reasonyAA0bC10ConnectionC_AA0bC15CloseReasonCodeOtF":{"name":"disconnected(connection:reason:)","abstract":"

Called when a WebSocket client disconnects from the server.

","parent_name":"WebSocketService"},"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP8received7message4fromy10Foundation4DataV_AA0bC10ConnectionCtF":{"name":"received(message:from:)","abstract":"

Called when a WebSocket client sent a binary message to the server to this WebSocketService.

","parent_name":"WebSocketService"},"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP8received7message4fromySS_AA0bC10ConnectionCtF":{"name":"received(message:from:)","abstract":"

Called when a WebSocket client sent a text message to the server to this WebSocketService.

","parent_name":"WebSocketService"},"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP17connectionTimeoutSiSgvp":{"name":"connectionTimeout","abstract":"

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.

","parent_name":"WebSocketService"},"Protocols/WebSocketService.html":{"name":"WebSocketService","abstract":"

The WebSocketService protocol is implemented by classes that wish to be WebSocket server side"},"Enums/WebSocketError.html#/s:15KituraWebSocket0bC5ErrorO13invalidOpCodeyACs5UInt8VcACmF":{"name":"invalidOpCode(_:)","abstract":"

An invalid opcode was received in a WebSocket frame

","parent_name":"WebSocketError"},"Enums/WebSocketError.html#/s:15KituraWebSocket0bC5ErrorO13unmaskedFrameyA2CmF":{"name":"unmaskedFrame","abstract":"

A frame was received that had an unmasked payload

","parent_name":"WebSocketError"},"Enums/WebSocketError.html#/s:15KituraWebSocket0bC5ErrorO11descriptionSSvp":{"name":"description","abstract":"

Generate a printable version of this enum.

","parent_name":"WebSocketError"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO16closedAbnormallyyA2CmF":{"name":"closedAbnormally","abstract":"

Closed abnormally (1006)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO16extensionMissingyA2CmF":{"name":"extensionMissing","abstract":"

An extension was missing that was required (1010)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO9goingAwayyA2CmF":{"name":"goingAway","abstract":"

Server is going away (1001)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO19invalidDataContentsyA2CmF":{"name":"invalidDataContents","abstract":"

Data within a message was invalid (1007)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO15invalidDataTypeyA2CmF":{"name":"invalidDataType","abstract":"

Message was of the incorrect type (binary/text) (1003)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO15messageTooLargeyA2CmF":{"name":"messageTooLarge","abstract":"

Message was too large (1009)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO6normalyA2CmF":{"name":"normal","abstract":"

Closed normally (1000)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO02noeF4SentyA2CmF":{"name":"noReasonCodeSent","abstract":"

No reason code sent with the close request (1005)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO15policyViolationyA2CmF":{"name":"policyViolation","abstract":"

A policy violation occurred (1008)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO13protocolErroryA2CmF":{"name":"protocolError","abstract":"

A protocol error occurred (1002)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO11serverErroryA2CmF":{"name":"serverError","abstract":"

The server had an error with the request (1011)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO11userDefinedyACs6UInt16VcACmF":{"name":"userDefined(_:)","abstract":"

This reason code is used to send application defined reason codes.

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO4codes6UInt16VyF":{"name":"code()","abstract":"

Get the sixteen bit integer code for a WebSocketCloseReasonCode instance

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO4from4codeACs6UInt16V_tFZ":{"name":"from(code:)","abstract":"

Convert a sixteen bit WebSocket close frame reason code to a WebSocketCloseReasonCode instance

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html":{"name":"WebSocketCloseReasonCode","abstract":"

The WebSocketCloseReasonCode enum defines the set of reason codes that a"},"Enums/WebSocketError.html":{"name":"WebSocketError","abstract":"

An error enum used when throwing errors within KituraWebSocket.

"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC2idSSvp":{"name":"id","abstract":"

Unique identifier for this WebSocketConnection

","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC7request0A3Net13ServerRequest_pvp":{"name":"request","abstract":"

The ServerRequest from the original protocol upgrade

","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC5close6reason11descriptionyAA0bC15CloseReasonCodeOSg_SSSgtF":{"name":"close(reason:description:)","abstract":"

Close a WebSocket connection by sending a close control command to the client optionally","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC4drop6reason11descriptionyAA0bC15CloseReasonCodeOSg_SSSgtF":{"name":"drop(reason:description:)","abstract":"

Forcefully close a WebSocket connection by sending a close control command to the client optionally","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC4ping11withMessageySSSg_tF":{"name":"ping(withMessage:)","abstract":"

Send a ping control frame to the client

","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC4send7message8asBinaryy10Foundation4DataV_SbtF":{"name":"send(message:asBinary:)","abstract":"

Send a message to the client contained in a Data struct.

","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC4send7messageySS_tF":{"name":"send(message:)","abstract":"

Send a text message to the client

","parent_name":"WebSocketConnection"},"Classes/WebSocket.html#/s:15KituraWebSocket0bC0C8register7service6onPathyAA0bC7Service_p_SStFZ":{"name":"register(service:onPath:)","abstract":"

Register a WebSocketService for a specific path

","parent_name":"WebSocket"},"Classes/WebSocket.html#/s:15KituraWebSocket0bC0C10unregister4pathySS_tFZ":{"name":"unregister(path:)","abstract":"

Unregister a WebSocketService for a specific path

","parent_name":"WebSocket"},"Classes/WSConnectionUpgradeFactory.html#/s:15KituraWebSocket26WSConnectionUpgradeFactoryC4nameSSvp":{"name":"name","abstract":"

The name of the protocol supported by this ConnectionUpgradeFactory.

","parent_name":"WSConnectionUpgradeFactory"},"Classes/WSConnectionUpgradeFactory.html#/s:15KituraWebSocket26WSConnectionUpgradeFactoryC7upgrade7handler7request8response0A3Net08IncomingC9Processor_pSg_SSSgtAH0lC7HandlerC_AH13ServerRequest_pAH0O8Response_ptF":{"name":"upgrade(handler:request:response:)","abstract":"

“Upgrade” a connection to the WebSocket protocol. Invoked by the KituraNet.ConnectionUpgrader when","parent_name":"WSConnectionUpgradeFactory"},"Classes/WSConnectionUpgradeFactory.html":{"name":"WSConnectionUpgradeFactory","abstract":"

The implementation of the ConnectionUpgradeFactory protocol for the WebSocket protocol."},"Classes/WebSocket.html":{"name":"WebSocket","abstract":"

Main class for the Kitura-WebSocket API. Used to register WebSocketService classes"},"Classes/WebSocketConnection.html":{"name":"WebSocketConnection","abstract":"

Represents a specific WebSocket connection. Provides a unique identifier for"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Enums.html":{"name":"Enumerations","abstract":"

The following enumerations are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"}} -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/docsets/KituraWebSocket.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/KituraWebSocket.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/docsets/KituraWebSocket.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Kitura-WebSocket/e79d57cc575e9df309e3c207ec1aa93d7aa61520/docs/img/spinner.gif -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | 61 | // KaTeX rendering 62 | if ("katex" in window) { 63 | $($('.math').each( (_, element) => { 64 | katex.render(element.textContent, element, { 65 | displayMode: $(element).hasClass('m-block'), 66 | throwOnError: false, 67 | trust: true 68 | }); 69 | })) 70 | } 71 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var $typeahead = $('[data-typeahead]'); 3 | var $form = $typeahead.parents('form'); 4 | var searchURL = $form.attr('action'); 5 | 6 | function displayTemplate(result) { 7 | return result.name; 8 | } 9 | 10 | function suggestionTemplate(result) { 11 | var t = '
'; 12 | t += '' + result.name + ''; 13 | if (result.parent_name) { 14 | t += '' + result.parent_name + ''; 15 | } 16 | t += '
'; 17 | return t; 18 | } 19 | 20 | $typeahead.one('focus', function() { 21 | $form.addClass('loading'); 22 | 23 | $.getJSON(searchURL).then(function(searchData) { 24 | const searchIndex = lunr(function() { 25 | this.ref('url'); 26 | this.field('name'); 27 | this.field('abstract'); 28 | for (const [url, doc] of Object.entries(searchData)) { 29 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 30 | } 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3, 37 | autoselect: true 38 | }, 39 | { 40 | limit: 10, 41 | display: displayTemplate, 42 | templates: { suggestion: suggestionTemplate }, 43 | source: function(query, sync) { 44 | const lcSearch = query.toLowerCase(); 45 | const results = searchIndex.query(function(q) { 46 | q.term(lcSearch, { boost: 100 }); 47 | q.term(lcSearch, { 48 | boost: 10, 49 | wildcard: lunr.Query.wildcard.TRAILING 50 | }); 51 | }).map(function(result) { 52 | var doc = searchData[result.ref]; 53 | doc.url = result.ref; 54 | return doc; 55 | }); 56 | sync(results); 57 | } 58 | } 59 | ); 60 | $form.removeClass('loading'); 61 | $typeahead.trigger('focus'); 62 | }); 63 | }); 64 | 65 | var baseURL = searchURL.slice(0, -"search.json".length); 66 | 67 | $typeahead.on('typeahead:select', function(e, result) { 68 | window.location = baseURL + result.url; 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /docs/search.json: -------------------------------------------------------------------------------- 1 | {"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP9connected10connectionyAA0bC10ConnectionC_tF":{"name":"connected(connection:)","abstract":"

Called when a WebSocket client connects to the server and is connected to a specific","parent_name":"WebSocketService"},"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP12disconnected10connection6reasonyAA0bC10ConnectionC_AA0bC15CloseReasonCodeOtF":{"name":"disconnected(connection:reason:)","abstract":"

Called when a WebSocket client disconnects from the server.

","parent_name":"WebSocketService"},"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP8received7message4fromy10Foundation4DataV_AA0bC10ConnectionCtF":{"name":"received(message:from:)","abstract":"

Called when a WebSocket client sent a binary message to the server to this WebSocketService.

","parent_name":"WebSocketService"},"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP8received7message4fromySS_AA0bC10ConnectionCtF":{"name":"received(message:from:)","abstract":"

Called when a WebSocket client sent a text message to the server to this WebSocketService.

","parent_name":"WebSocketService"},"Protocols/WebSocketService.html#/s:15KituraWebSocket0bC7ServiceP17connectionTimeoutSiSgvp":{"name":"connectionTimeout","abstract":"

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.

","parent_name":"WebSocketService"},"Protocols/WebSocketService.html":{"name":"WebSocketService","abstract":"

The WebSocketService protocol is implemented by classes that wish to be WebSocket server side"},"Enums/WebSocketError.html#/s:15KituraWebSocket0bC5ErrorO13invalidOpCodeyACs5UInt8VcACmF":{"name":"invalidOpCode(_:)","abstract":"

An invalid opcode was received in a WebSocket frame

","parent_name":"WebSocketError"},"Enums/WebSocketError.html#/s:15KituraWebSocket0bC5ErrorO13unmaskedFrameyA2CmF":{"name":"unmaskedFrame","abstract":"

A frame was received that had an unmasked payload

","parent_name":"WebSocketError"},"Enums/WebSocketError.html#/s:15KituraWebSocket0bC5ErrorO11descriptionSSvp":{"name":"description","abstract":"

Generate a printable version of this enum.

","parent_name":"WebSocketError"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO16closedAbnormallyyA2CmF":{"name":"closedAbnormally","abstract":"

Closed abnormally (1006)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO16extensionMissingyA2CmF":{"name":"extensionMissing","abstract":"

An extension was missing that was required (1010)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO9goingAwayyA2CmF":{"name":"goingAway","abstract":"

Server is going away (1001)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO19invalidDataContentsyA2CmF":{"name":"invalidDataContents","abstract":"

Data within a message was invalid (1007)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO15invalidDataTypeyA2CmF":{"name":"invalidDataType","abstract":"

Message was of the incorrect type (binary/text) (1003)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO15messageTooLargeyA2CmF":{"name":"messageTooLarge","abstract":"

Message was too large (1009)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO6normalyA2CmF":{"name":"normal","abstract":"

Closed normally (1000)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO02noeF4SentyA2CmF":{"name":"noReasonCodeSent","abstract":"

No reason code sent with the close request (1005)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO15policyViolationyA2CmF":{"name":"policyViolation","abstract":"

A policy violation occurred (1008)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO13protocolErroryA2CmF":{"name":"protocolError","abstract":"

A protocol error occurred (1002)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO11serverErroryA2CmF":{"name":"serverError","abstract":"

The server had an error with the request (1011)

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO11userDefinedyACs6UInt16VcACmF":{"name":"userDefined(_:)","abstract":"

This reason code is used to send application defined reason codes.

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO4codes6UInt16VyF":{"name":"code()","abstract":"

Get the sixteen bit integer code for a WebSocketCloseReasonCode instance

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html#/s:15KituraWebSocket0bC15CloseReasonCodeO4from4codeACs6UInt16V_tFZ":{"name":"from(code:)","abstract":"

Convert a sixteen bit WebSocket close frame reason code to a WebSocketCloseReasonCode instance

","parent_name":"WebSocketCloseReasonCode"},"Enums/WebSocketCloseReasonCode.html":{"name":"WebSocketCloseReasonCode","abstract":"

The WebSocketCloseReasonCode enum defines the set of reason codes that a"},"Enums/WebSocketError.html":{"name":"WebSocketError","abstract":"

An error enum used when throwing errors within KituraWebSocket.

"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC2idSSvp":{"name":"id","abstract":"

Unique identifier for this WebSocketConnection

","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC7request0A3Net13ServerRequest_pvp":{"name":"request","abstract":"

The ServerRequest from the original protocol upgrade

","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC5close6reason11descriptionyAA0bC15CloseReasonCodeOSg_SSSgtF":{"name":"close(reason:description:)","abstract":"

Close a WebSocket connection by sending a close control command to the client optionally","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC4drop6reason11descriptionyAA0bC15CloseReasonCodeOSg_SSSgtF":{"name":"drop(reason:description:)","abstract":"

Forcefully close a WebSocket connection by sending a close control command to the client optionally","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC4ping11withMessageySSSg_tF":{"name":"ping(withMessage:)","abstract":"

Send a ping control frame to the client

","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC4send7message8asBinaryy10Foundation4DataV_SbtF":{"name":"send(message:asBinary:)","abstract":"

Send a message to the client contained in a Data struct.

","parent_name":"WebSocketConnection"},"Classes/WebSocketConnection.html#/s:15KituraWebSocket0bC10ConnectionC4send7messageySS_tF":{"name":"send(message:)","abstract":"

Send a text message to the client

","parent_name":"WebSocketConnection"},"Classes/WebSocket.html#/s:15KituraWebSocket0bC0C8register7service6onPathyAA0bC7Service_p_SStFZ":{"name":"register(service:onPath:)","abstract":"

Register a WebSocketService for a specific path

","parent_name":"WebSocket"},"Classes/WebSocket.html#/s:15KituraWebSocket0bC0C10unregister4pathySS_tFZ":{"name":"unregister(path:)","abstract":"

Unregister a WebSocketService for a specific path

","parent_name":"WebSocket"},"Classes/WSConnectionUpgradeFactory.html#/s:15KituraWebSocket26WSConnectionUpgradeFactoryC4nameSSvp":{"name":"name","abstract":"

The name of the protocol supported by this ConnectionUpgradeFactory.

","parent_name":"WSConnectionUpgradeFactory"},"Classes/WSConnectionUpgradeFactory.html#/s:15KituraWebSocket26WSConnectionUpgradeFactoryC7upgrade7handler7request8response0A3Net08IncomingC9Processor_pSg_SSSgtAH0lC7HandlerC_AH13ServerRequest_pAH0O8Response_ptF":{"name":"upgrade(handler:request:response:)","abstract":"

“Upgrade” a connection to the WebSocket protocol. Invoked by the KituraNet.ConnectionUpgrader when","parent_name":"WSConnectionUpgradeFactory"},"Classes/WSConnectionUpgradeFactory.html":{"name":"WSConnectionUpgradeFactory","abstract":"

The implementation of the ConnectionUpgradeFactory protocol for the WebSocket protocol."},"Classes/WebSocket.html":{"name":"WebSocket","abstract":"

Main class for the Kitura-WebSocket API. Used to register WebSocketService classes"},"Classes/WebSocketConnection.html":{"name":"WebSocketConnection","abstract":"

Represents a specific WebSocket connection. Provides a unique identifier for"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Enums.html":{"name":"Enumerations","abstract":"

The following enumerations are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"}} -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/dannys/projects/kitura/Kitura-WebSocket" 6 | } --------------------------------------------------------------------------------