From edecf5507d9458997a06bdaf159aff11fedb9e57 Mon Sep 17 00:00:00 2001
From: fwcd
Date: Sun, 20 Feb 2022 22:24:15 +0100
Subject: [PATCH 1/8] Add a test server
---
Package.resolved | 126 ++++++++++++++++++
Package.swift | 28 +++-
README.md | 42 +++---
Resources/Views/index.leaf | 9 ++
Sources/LighthouseClient/Connection.swift | 1 +
Sources/LighthouseClient/Constants.swift | 3 -
Sources/LighthouseDemo/LighthouseDemo.swift | 1 +
.../Authentication.swift | 0
.../Color.swift | 0
Sources/LighthouseProtocol/Constants.swift | 3 +
.../Display.swift | 0
.../Protocol.swift | 34 ++++-
Sources/LighthouseTestServer/configure.swift | 12 ++
Sources/LighthouseTestServer/main.swift | 8 ++
Sources/LighthouseTestServer/routes.swift | 7 +
Sources/Package.swift | 34 +++++
16 files changed, 282 insertions(+), 26 deletions(-)
create mode 100644 Resources/Views/index.leaf
rename Sources/{LighthouseClient => LighthouseProtocol}/Authentication.swift (100%)
rename Sources/{LighthouseClient => LighthouseProtocol}/Color.swift (100%)
create mode 100644 Sources/LighthouseProtocol/Constants.swift
rename Sources/{LighthouseClient => LighthouseProtocol}/Display.swift (100%)
rename Sources/{LighthouseClient => LighthouseProtocol}/Protocol.swift (73%)
create mode 100644 Sources/LighthouseTestServer/configure.swift
create mode 100644 Sources/LighthouseTestServer/main.swift
create mode 100644 Sources/LighthouseTestServer/routes.swift
create mode 100644 Sources/Package.swift
diff --git a/Package.resolved b/Package.resolved
index 2e0bdcb..76cfaf2 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -1,6 +1,51 @@
{
"object": {
"pins": [
+ {
+ "package": "async-http-client",
+ "repositoryURL": "https://github.com/swift-server/async-http-client.git",
+ "state": {
+ "branch": null,
+ "revision": "7a4dfe026f6ee0f8ad741b58df74c60af296365d",
+ "version": "1.9.0"
+ }
+ },
+ {
+ "package": "async-kit",
+ "repositoryURL": "https://github.com/vapor/async-kit.git",
+ "state": {
+ "branch": null,
+ "revision": "e2f741640364c1d271405da637029ea6a33f754e",
+ "version": "1.11.1"
+ }
+ },
+ {
+ "package": "console-kit",
+ "repositoryURL": "https://github.com/vapor/console-kit.git",
+ "state": {
+ "branch": null,
+ "revision": "75ea3b627d88221440b878e5dfccc73fd06842ed",
+ "version": "4.2.7"
+ }
+ },
+ {
+ "package": "leaf",
+ "repositoryURL": "https://github.com/vapor/leaf.git",
+ "state": {
+ "branch": null,
+ "revision": "41741b782ac49959af2f7612661154326ac24d00",
+ "version": "4.1.5"
+ }
+ },
+ {
+ "package": "leaf-kit",
+ "repositoryURL": "https://github.com/vapor/leaf-kit.git",
+ "state": {
+ "branch": null,
+ "revision": "983fcbe89e7153c4d5870bdc76bf57a56817225f",
+ "version": "1.4.0"
+ }
+ },
{
"package": "MessagePack",
"repositoryURL": "https://github.com/Flight-School/MessagePack.git",
@@ -10,6 +55,24 @@
"version": "1.2.4"
}
},
+ {
+ "package": "multipart-kit",
+ "repositoryURL": "https://github.com/vapor/multipart-kit.git",
+ "state": {
+ "branch": null,
+ "revision": "2dd9368a3c9580792b77c7ef364f3735909d9996",
+ "version": "4.5.1"
+ }
+ },
+ {
+ "package": "routing-kit",
+ "repositoryURL": "https://github.com/vapor/routing-kit.git",
+ "state": {
+ "branch": null,
+ "revision": "5603b81ceb744b8318feab1e60943704977a866b",
+ "version": "4.3.1"
+ }
+ },
{
"package": "swift-argument-parser",
"repositoryURL": "https://github.com/apple/swift-argument-parser.git",
@@ -19,6 +82,24 @@
"version": "1.0.3"
}
},
+ {
+ "package": "swift-backtrace",
+ "repositoryURL": "https://github.com/swift-server/swift-backtrace.git",
+ "state": {
+ "branch": null,
+ "revision": "d3e04a9d4b3833363fb6192065b763310b156d54",
+ "version": "1.3.1"
+ }
+ },
+ {
+ "package": "swift-crypto",
+ "repositoryURL": "https://github.com/apple/swift-crypto.git",
+ "state": {
+ "branch": null,
+ "revision": "a8911e0fadc25aef1071d582355bd1037a176060",
+ "version": "2.0.4"
+ }
+ },
{
"package": "swift-log",
"repositoryURL": "https://github.com/apple/swift-log.git",
@@ -28,6 +109,15 @@
"version": "1.4.2"
}
},
+ {
+ "package": "swift-metrics",
+ "repositoryURL": "https://github.com/apple/swift-metrics.git",
+ "state": {
+ "branch": null,
+ "revision": "3edd2f57afc4e68e23c3e4956bc8b65ca6b5b2ff",
+ "version": "2.2.0"
+ }
+ },
{
"package": "swift-nio",
"repositoryURL": "https://github.com/apple/swift-nio.git",
@@ -37,6 +127,24 @@
"version": "2.38.0"
}
},
+ {
+ "package": "swift-nio-extras",
+ "repositoryURL": "https://github.com/apple/swift-nio-extras.git",
+ "state": {
+ "branch": null,
+ "revision": "f73ca5ee9c6806800243f1ac415fcf82de9a4c91",
+ "version": "1.10.2"
+ }
+ },
+ {
+ "package": "swift-nio-http2",
+ "repositoryURL": "https://github.com/apple/swift-nio-http2.git",
+ "state": {
+ "branch": null,
+ "revision": "000ca94f9de92c95b9ac85d44600b7b0fe25a3e5",
+ "version": "1.19.2"
+ }
+ },
{
"package": "swift-nio-ssl",
"repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
@@ -46,6 +154,24 @@
"version": "2.17.2"
}
},
+ {
+ "package": "swift-nio-transport-services",
+ "repositoryURL": "https://github.com/apple/swift-nio-transport-services.git",
+ "state": {
+ "branch": null,
+ "revision": "8ab824b140d0ebcd87e9149266ddc353e3705a3e",
+ "version": "1.11.4"
+ }
+ },
+ {
+ "package": "vapor",
+ "repositoryURL": "https://github.com/vapor/vapor.git",
+ "state": {
+ "branch": null,
+ "revision": "18e9419cae5049e43ca1e8002ca3cf0449f2c8ed",
+ "version": "4.55.0"
+ }
+ },
{
"package": "websocket-kit",
"repositoryURL": "https://github.com/vapor/websocket-kit.git",
diff --git a/Package.swift b/Package.swift
index 84c280a..b7a7a76 100644
--- a/Package.swift
+++ b/Package.swift
@@ -10,12 +10,16 @@ let package = Package(
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "LighthouseClient",
- targets: ["LighthouseClient"]
+ targets: ["LighthouseProtocol", "LighthouseClient"]
),
.executable(
name: "LighthouseDemo",
targets: ["LighthouseDemo"]
- )
+ ),
+ .executable(
+ name: "LighthouseTestServer",
+ targets: ["LighthouseTestServer"]
+ ),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
@@ -23,13 +27,22 @@ let package = Package(
.package(url: "https://github.com/Flight-School/MessagePack.git", from: "1.2.4"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.4.2"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.3"),
+ .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
+ .package(url: "https://github.com/vapor/leaf.git", from: "4.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
+ .target(
+ name: "LighthouseProtocol",
+ dependencies: [
+ .product(name: "Logging", package: "swift-log"),
+ ]
+ ),
.target(
name: "LighthouseClient",
dependencies: [
+ .target(name: "LighthouseProtocol"),
.product(name: "WebSocketKit", package: "websocket-kit"),
.product(name: "MessagePack", package: "MessagePack"),
.product(name: "Logging", package: "swift-log"),
@@ -38,11 +51,22 @@ let package = Package(
.executableTarget(
name: "LighthouseDemo",
dependencies: [
+ .target(name: "LighthouseProtocol"),
.target(name: "LighthouseClient"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Logging", package: "swift-log"),
]
),
+ .executableTarget(
+ name: "LighthouseTestServer",
+ dependencies: [
+ .target(name: "LighthouseProtocol"),
+ .product(name: "ArgumentParser", package: "swift-argument-parser"),
+ .product(name: "Logging", package: "swift-log"),
+ .product(name: "Vapor", package: "vapor"),
+ .product(name: "Leaf", package: "leaf"),
+ ]
+ ),
// .testTarget(
// name: "LighthouseClientTests",
// dependencies: [
diff --git a/README.md b/README.md
index 0d682cf..494c376 100644
--- a/README.md
+++ b/README.md
@@ -7,25 +7,29 @@ An API client for a light installation at the University of Kiel using Swift 5.5
## Example
```swift
-// Prepare connection
-let conn = Connection(authentication: Authentication(
- username: "[your username]",
- token: "[your token]"
-))
-
-// Handle incoming input events
-conn.onInput { input in
- print("Got input \(input)")
-}
-
-// Connect to the lighthouse server and request events
-try await conn.connect()
-try await conn.requestStream()
-
-// Repeatedly send colored displays to the lighthouse
-while true {
- try await conn.send(display: Display(fill: .random()))
- try await Task.sleep(nanoseconds: 1_000_000_000)
+import LighthouseClient
+
+func runApp() async throws {
+ // Prepare connection
+ let conn = Connection(authentication: Authentication(
+ username: "[your username]",
+ token: "[your token]"
+ ))
+
+ // Handle incoming input events
+ conn.onInput { input in
+ print("Got input \(input)")
+ }
+
+ // Connect to the lighthouse server and request events
+ try await conn.connect()
+ try await conn.requestStream()
+
+ // Repeatedly send colored displays to the lighthouse
+ while true {
+ try await conn.send(display: Display(fill: .random()))
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ }
}
```
diff --git a/Resources/Views/index.leaf b/Resources/Views/index.leaf
new file mode 100644
index 0000000..515521d
--- /dev/null
+++ b/Resources/Views/index.leaf
@@ -0,0 +1,9 @@
+
+
+
+ Lighthouse Test Server
+
+
+ Test
+
+
diff --git a/Sources/LighthouseClient/Connection.swift b/Sources/LighthouseClient/Connection.swift
index 1233284..a571ce6 100644
--- a/Sources/LighthouseClient/Connection.swift
+++ b/Sources/LighthouseClient/Connection.swift
@@ -3,6 +3,7 @@ import Logging
import MessagePack
import NIO
import WebSocketKit
+import LighthouseProtocol
private let log = Logger(label: "LighthouseClient.Connection")
diff --git a/Sources/LighthouseClient/Constants.swift b/Sources/LighthouseClient/Constants.swift
index 301e1f5..04a7f10 100644
--- a/Sources/LighthouseClient/Constants.swift
+++ b/Sources/LighthouseClient/Constants.swift
@@ -1,6 +1,3 @@
import Foundation
-public let lighthouseRows: Int = 14
-public let lighthouseCols: Int = 28
-public let lighthouseSize: Int = lighthouseRows * lighthouseCols
public let lighthouseUrl: URL = URL(string: "wss://lighthouse.uni-kiel.de/websocket")!
diff --git a/Sources/LighthouseDemo/LighthouseDemo.swift b/Sources/LighthouseDemo/LighthouseDemo.swift
index 80fc455..824a78c 100644
--- a/Sources/LighthouseDemo/LighthouseDemo.swift
+++ b/Sources/LighthouseDemo/LighthouseDemo.swift
@@ -2,6 +2,7 @@ import ArgumentParser
import Dispatch
import Foundation
import Logging
+import LighthouseProtocol
import LighthouseClient
let log = Logger(label: "LighthouseDemo")
diff --git a/Sources/LighthouseClient/Authentication.swift b/Sources/LighthouseProtocol/Authentication.swift
similarity index 100%
rename from Sources/LighthouseClient/Authentication.swift
rename to Sources/LighthouseProtocol/Authentication.swift
diff --git a/Sources/LighthouseClient/Color.swift b/Sources/LighthouseProtocol/Color.swift
similarity index 100%
rename from Sources/LighthouseClient/Color.swift
rename to Sources/LighthouseProtocol/Color.swift
diff --git a/Sources/LighthouseProtocol/Constants.swift b/Sources/LighthouseProtocol/Constants.swift
new file mode 100644
index 0000000..44b6f92
--- /dev/null
+++ b/Sources/LighthouseProtocol/Constants.swift
@@ -0,0 +1,3 @@
+public let lighthouseRows: Int = 14
+public let lighthouseCols: Int = 28
+public let lighthouseSize: Int = lighthouseRows * lighthouseCols
diff --git a/Sources/LighthouseClient/Display.swift b/Sources/LighthouseProtocol/Display.swift
similarity index 100%
rename from Sources/LighthouseClient/Display.swift
rename to Sources/LighthouseProtocol/Display.swift
diff --git a/Sources/LighthouseClient/Protocol.swift b/Sources/LighthouseProtocol/Protocol.swift
similarity index 73%
rename from Sources/LighthouseClient/Protocol.swift
rename to Sources/LighthouseProtocol/Protocol.swift
index d83f9ef..9631564 100644
--- a/Sources/LighthouseClient/Protocol.swift
+++ b/Sources/LighthouseProtocol/Protocol.swift
@@ -56,9 +56,25 @@ public enum Protocol {
public var requestId: Int
public var verb: String
public var path: [String]
- public var meta: [String: String] = [:]
+ public var meta: [String: String]
public var authentication: Authentication
- public var payload: Payload = .other
+ public var payload: Payload
+
+ public init(
+ requestId: Int,
+ verb: String,
+ path: [String],
+ meta: [String: String] = [:],
+ authentication: Authentication,
+ payload: Payload
+ ) {
+ self.requestId = requestId
+ self.verb = verb
+ self.path = path
+ self.meta = meta
+ self.authentication = authentication
+ self.payload = payload
+ }
}
/// A message originating from the lighthouse server.
@@ -76,5 +92,19 @@ public enum Protocol {
public var warnings: [String]?
public var response: String?
public var payload: Payload
+
+ public init(
+ code: Int,
+ requestId: Int,
+ warnings: [String]? = nil,
+ response: String? = nil,
+ payload: Payload
+ ) {
+ self.code = code
+ self.requestId = requestId
+ self.warnings = warnings
+ self.response = response
+ self.payload = payload
+ }
}
}
diff --git a/Sources/LighthouseTestServer/configure.swift b/Sources/LighthouseTestServer/configure.swift
new file mode 100644
index 0000000..79da61f
--- /dev/null
+++ b/Sources/LighthouseTestServer/configure.swift
@@ -0,0 +1,12 @@
+import Vapor
+import Leaf
+
+public func configure(_ app: Application) throws {
+ // Serve from /Public
+ app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
+
+ app.views.use(.leaf)
+
+ // Register routes
+ try routes(app)
+}
diff --git a/Sources/LighthouseTestServer/main.swift b/Sources/LighthouseTestServer/main.swift
new file mode 100644
index 0000000..a291b28
--- /dev/null
+++ b/Sources/LighthouseTestServer/main.swift
@@ -0,0 +1,8 @@
+import Vapor
+
+var env = try Environment.detect()
+try LoggingSystem.bootstrap(from: &env)
+let app = Application(env)
+defer { app.shutdown() }
+try configure(app)
+try app.run()
diff --git a/Sources/LighthouseTestServer/routes.swift b/Sources/LighthouseTestServer/routes.swift
new file mode 100644
index 0000000..5e45ed4
--- /dev/null
+++ b/Sources/LighthouseTestServer/routes.swift
@@ -0,0 +1,7 @@
+import Vapor
+
+func routes(_ app: Application) throws {
+ app.get { req in
+ req.view.render("index")
+ }
+}
diff --git a/Sources/Package.swift b/Sources/Package.swift
new file mode 100644
index 0000000..33c4609
--- /dev/null
+++ b/Sources/Package.swift
@@ -0,0 +1,34 @@
+// swift-tools-version:5.5
+import PackageDescription
+
+let package = Package(
+ name: "LighthouseTestServer",
+ platforms: [
+ .macOS(.v10_15)
+ ],
+ dependencies: [
+ // 💧 A server-side Swift web framework.
+ .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
+ .package(url: "https://github.com/vapor/leaf.git", from: "4.0.0"),
+ ],
+ targets: [
+ .target(
+ name: "App",
+ dependencies: [
+ .product(name: "Leaf", package: "leaf"),
+ .product(name: "Vapor", package: "vapor")
+ ],
+ swiftSettings: [
+ // Enable better optimizations when building in Release configuration. Despite the use of
+ // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release
+ // builds. See for details.
+ .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
+ ]
+ ),
+ .executableTarget(name: "Run", dependencies: [.target(name: "App")]),
+ .testTarget(name: "AppTests", dependencies: [
+ .target(name: "App"),
+ .product(name: "XCTVapor", package: "vapor"),
+ ])
+ ]
+)
From 2d9d3728f43d30295028192cf811ea0bc42e0e3c Mon Sep 17 00:00:00 2001
From: fwcd
Date: Sun, 20 Feb 2022 22:39:36 +0100
Subject: [PATCH 2/8] Actually use the arg URL in the demo
---
Sources/LighthouseDemo/LighthouseDemo.swift | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Sources/LighthouseDemo/LighthouseDemo.swift b/Sources/LighthouseDemo/LighthouseDemo.swift
index 824a78c..33921ae 100644
--- a/Sources/LighthouseDemo/LighthouseDemo.swift
+++ b/Sources/LighthouseDemo/LighthouseDemo.swift
@@ -5,8 +5,8 @@ import Logging
import LighthouseProtocol
import LighthouseClient
-let log = Logger(label: "LighthouseDemo")
-let env = ProcessInfo.processInfo.environment
+private let log = Logger(label: "LighthouseDemo")
+private let env = ProcessInfo.processInfo.environment
@main
struct LighthouseDemo: ParsableCommand {
@@ -25,7 +25,7 @@ struct LighthouseDemo: ParsableCommand {
// Prepare connection
let auth = Authentication(username: username, token: token)
- let conn = Connection(authentication: auth)
+ let conn = Connection(authentication: auth, url: url)
// Handle incoming input events
conn.onInput { input in
From 576708048a0a85a644d406f8654c667138488fe4 Mon Sep 17 00:00:00 2001
From: fwcd
Date: Sun, 20 Feb 2022 22:39:49 +0100
Subject: [PATCH 3/8] Handle WebSocket connections in the test server
---
Package.swift | 5 +-
.../ConnectionHandler.swift | 51 +++++++++++++++++++
Sources/LighthouseTestServer/routes.swift | 5 ++
Sources/Package.swift | 34 -------------
4 files changed, 59 insertions(+), 36 deletions(-)
create mode 100644 Sources/LighthouseTestServer/ConnectionHandler.swift
delete mode 100644 Sources/Package.swift
diff --git a/Package.swift b/Package.swift
index b7a7a76..737fe9e 100644
--- a/Package.swift
+++ b/Package.swift
@@ -43,9 +43,9 @@ let package = Package(
name: "LighthouseClient",
dependencies: [
.target(name: "LighthouseProtocol"),
- .product(name: "WebSocketKit", package: "websocket-kit"),
- .product(name: "MessagePack", package: "MessagePack"),
.product(name: "Logging", package: "swift-log"),
+ .product(name: "MessagePack", package: "MessagePack"),
+ .product(name: "WebSocketKit", package: "websocket-kit"),
]
),
.executableTarget(
@@ -63,6 +63,7 @@ let package = Package(
.target(name: "LighthouseProtocol"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Logging", package: "swift-log"),
+ .product(name: "MessagePack", package: "MessagePack"),
.product(name: "Vapor", package: "vapor"),
.product(name: "Leaf", package: "leaf"),
]
diff --git a/Sources/LighthouseTestServer/ConnectionHandler.swift b/Sources/LighthouseTestServer/ConnectionHandler.swift
new file mode 100644
index 0000000..84df735
--- /dev/null
+++ b/Sources/LighthouseTestServer/ConnectionHandler.swift
@@ -0,0 +1,51 @@
+import NIO
+import MessagePack
+import Vapor
+import LighthouseProtocol
+
+private let log = Logger(label: "LighthouseTestServer.MessagingHandler")
+
+/// Handles the server side of the LighthouseTestServer's web sockets.
+class ConnectionHandler {
+ private var clients: [UUID: ClientState] = [:]
+
+ private class ClientState {
+ private let ws: WebSocket
+ var name: String? = nil
+
+ init(ws: WebSocket) {
+ self.ws = ws
+ }
+
+ func handleReceive(of data: Data) {
+ do {
+ let message = try MessagePackDecoder().decode(Protocol.ClientMessage.self, from: data)
+
+ log.info("Got \(message)")
+ // TODO: Handle it
+ } catch {
+ log.warning("Error while decoding message: \(error)")
+ }
+ }
+ }
+
+ func connect(_ ws: WebSocket) {
+ let uuid = UUID()
+ clients[uuid] = ClientState(ws: ws)
+
+ log.info("Opened connection to \(uuid)")
+
+ ws.onBinary { [weak self] _, buf in
+ guard let data = buf.getData(at: 0, length: buf.readableBytes) else {
+ log.warning("Could not read data from WebSocket")
+ return
+ }
+ self?.clients[uuid]?.handleReceive(of: data)
+ }
+
+ ws.onClose.whenComplete { [weak self] _ in
+ self?.clients[uuid] = nil
+ log.info("Closed connection to \(uuid)")
+ }
+ }
+}
diff --git a/Sources/LighthouseTestServer/routes.swift b/Sources/LighthouseTestServer/routes.swift
index 5e45ed4..44e69af 100644
--- a/Sources/LighthouseTestServer/routes.swift
+++ b/Sources/LighthouseTestServer/routes.swift
@@ -4,4 +4,9 @@ func routes(_ app: Application) throws {
app.get { req in
req.view.render("index")
}
+
+ let handler = ConnectionHandler()
+ app.webSocket("websocket") { req, ws in
+ handler.connect(ws)
+ }
}
diff --git a/Sources/Package.swift b/Sources/Package.swift
deleted file mode 100644
index 33c4609..0000000
--- a/Sources/Package.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-// swift-tools-version:5.5
-import PackageDescription
-
-let package = Package(
- name: "LighthouseTestServer",
- platforms: [
- .macOS(.v10_15)
- ],
- dependencies: [
- // 💧 A server-side Swift web framework.
- .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
- .package(url: "https://github.com/vapor/leaf.git", from: "4.0.0"),
- ],
- targets: [
- .target(
- name: "App",
- dependencies: [
- .product(name: "Leaf", package: "leaf"),
- .product(name: "Vapor", package: "vapor")
- ],
- swiftSettings: [
- // Enable better optimizations when building in Release configuration. Despite the use of
- // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release
- // builds. See for details.
- .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
- ]
- ),
- .executableTarget(name: "Run", dependencies: [.target(name: "App")]),
- .testTarget(name: "AppTests", dependencies: [
- .target(name: "App"),
- .product(name: "XCTVapor", package: "vapor"),
- ])
- ]
-)
From 7b88d2fa843a591410c7583f7f6258e9fd96ea4a Mon Sep 17 00:00:00 2001
From: fwcd
Date: Sun, 20 Feb 2022 22:57:31 +0100
Subject: [PATCH 4/8] Handle basic requests in test server
---
Sources/LighthouseProtocol/Protocol.swift | 2 +-
.../ConnectionHandler.swift | 59 +++++++++++++++----
2 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/Sources/LighthouseProtocol/Protocol.swift b/Sources/LighthouseProtocol/Protocol.swift
index 9631564..e576cce 100644
--- a/Sources/LighthouseProtocol/Protocol.swift
+++ b/Sources/LighthouseProtocol/Protocol.swift
@@ -98,7 +98,7 @@ public enum Protocol {
requestId: Int,
warnings: [String]? = nil,
response: String? = nil,
- payload: Payload
+ payload: Payload = .other
) {
self.code = code
self.requestId = requestId
diff --git a/Sources/LighthouseTestServer/ConnectionHandler.swift b/Sources/LighthouseTestServer/ConnectionHandler.swift
index 84df735..eda1b03 100644
--- a/Sources/LighthouseTestServer/ConnectionHandler.swift
+++ b/Sources/LighthouseTestServer/ConnectionHandler.swift
@@ -11,28 +11,36 @@ class ConnectionHandler {
private class ClientState {
private let ws: WebSocket
+ var receivesStream: Bool = false
+ var display: Display = .init()
var name: String? = nil
init(ws: WebSocket) {
self.ws = ws
}
- func handleReceive(of data: Data) {
- do {
- let message = try MessagePackDecoder().decode(Protocol.ClientMessage.self, from: data)
+ func respond(to requestId: Int, code: Int, response: String? = nil) throws {
+ log.info("Responding with \(code) (id: \(requestId))")
+ try send(message: Protocol.ServerMessage(
+ code: code,
+ requestId: requestId,
+ response: response
+ ))
+ }
- log.info("Got \(message)")
- // TODO: Handle it
- } catch {
- log.warning("Error while decoding message: \(error)")
- }
+ func send(message: Message) throws where Message: Encodable {
+ let data = try MessagePackEncoder().encode(message)
+ send(data: data)
+ }
+
+ func send(data: Data) {
+ ws.send(Array(data))
}
}
func connect(_ ws: WebSocket) {
let uuid = UUID()
clients[uuid] = ClientState(ws: ws)
-
log.info("Opened connection to \(uuid)")
ws.onBinary { [weak self] _, buf in
@@ -40,7 +48,7 @@ class ConnectionHandler {
log.warning("Could not read data from WebSocket")
return
}
- self?.clients[uuid]?.handleReceive(of: data)
+ self?.handleReceive(of: data, for: uuid)
}
ws.onClose.whenComplete { [weak self] _ in
@@ -48,4 +56,35 @@ class ConnectionHandler {
log.info("Closed connection to \(uuid)")
}
}
+
+ func handleReceive(of data: Data, for uuid: UUID) {
+ do {
+ guard let client = clients[uuid] else {
+ log.warning("No client associated with \(uuid)")
+ return
+ }
+
+ let message = try MessagePackDecoder().decode(Protocol.ClientMessage.self, from: data)
+ log.info("Got \(message.verb) \(message.path.joined(separator: "/")) (id: \(message.requestId))")
+
+ let name = message.authentication.username
+ client.name = name
+
+ switch message.verb {
+ case "PUT":
+ log.info("Got a PUT, forwarding it to others")
+ // TODO
+ case "STREAM":
+ client.receivesStream = true
+ log.info("Enabled stream for \(name)")
+ try client.respond(to: message.requestId, code: 200)
+ default:
+ log.warning("Got unknown verb '\(message.verb)'")
+ try client.respond(to: message.requestId, code: 400, response: "Bad Request")
+ break
+ }
+ } catch {
+ log.warning("Error while handling message: \(error)")
+ }
+ }
}
From 781553d1ab93977b49f051c0ea6d478c8e84b125 Mon Sep 17 00:00:00 2001
From: fwcd
Date: Sun, 20 Feb 2022 23:03:51 +0100
Subject: [PATCH 5/8] Handle PUT and STREAM requests in the test server
---
.../ConnectionHandler.swift | 25 ++++++++++++++-----
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/Sources/LighthouseTestServer/ConnectionHandler.swift b/Sources/LighthouseTestServer/ConnectionHandler.swift
index eda1b03..6c95ccd 100644
--- a/Sources/LighthouseTestServer/ConnectionHandler.swift
+++ b/Sources/LighthouseTestServer/ConnectionHandler.swift
@@ -28,6 +28,14 @@ class ConnectionHandler {
))
}
+ func send(display: Display) throws {
+ try send(message: Protocol.ServerMessage(
+ code: 200,
+ requestId: 0, // TODO: Set this to some value?
+ payload: .display(display)
+ ))
+ }
+
func send(message: Message) throws where Message: Encodable {
let data = try MessagePackEncoder().encode(message)
send(data: data)
@@ -70,16 +78,21 @@ class ConnectionHandler {
let name = message.authentication.username
client.name = name
- switch message.verb {
- case "PUT":
- log.info("Got a PUT, forwarding it to others")
- // TODO
- case "STREAM":
+ switch (message.verb, message.payload) {
+ case ("PUT", .display(let display)):
+ log.info("Updating \(name)'s display and notifying streaming clients...")
+ client.display = display
+ try client.respond(to: message.requestId, code: 200)
+
+ for rcvClient in clients.values where rcvClient.receivesStream {
+ try rcvClient.send(display: client.display)
+ }
+ case ("STREAM", _):
client.receivesStream = true
log.info("Enabled stream for \(name)")
try client.respond(to: message.requestId, code: 200)
default:
- log.warning("Got unknown verb '\(message.verb)'")
+ log.warning("Got unknown request \(message.verb) with payload \(message.payload)")
try client.respond(to: message.requestId, code: 400, response: "Bad Request")
break
}
From 116fd68fede332c4e419b50d3f4e96eef2977805 Mon Sep 17 00:00:00 2001
From: fwcd
Date: Sun, 20 Feb 2022 23:47:11 +0100
Subject: [PATCH 6/8] Implement web client for test server
---
Public/scripts.js | 65 ++++++++++++++++++++++++++++++++++++++
Resources/Views/index.leaf | 18 +++++++++--
2 files changed, 81 insertions(+), 2 deletions(-)
create mode 100644 Public/scripts.js
diff --git a/Public/scripts.js b/Public/scripts.js
new file mode 100644
index 0000000..6af2cca
--- /dev/null
+++ b/Public/scripts.js
@@ -0,0 +1,65 @@
+let connection = null;
+let auth = null;
+
+function setUpConnection() {
+ connection = new WebSocket(`${location.origin.replace(/^http/, "ws")}/websocket`);
+ connection.binaryType = "arraybuffer";
+
+ connection.addEventListener("open", () => {
+ console.log("Connected!");
+ });
+
+ connection.addEventListener("message", event => {
+ try {
+ const message = MessagePack.decode(new Uint8Array(event.data));
+
+ if (message.PAYL instanceof Uint8Array) {
+ const display = document.getElementById("display");
+ display.innerText = message.PAYL.map(x => `${x}`).join(", ");
+ }
+ } catch (e) {
+ console.log(`Error while decoding message from WebSocket: ${e}`);
+ }
+ });
+}
+
+function setUpFormListener() {
+ const form = document.getElementById("auth-form");
+ const fieldset = document.getElementById("auth-form-fieldset");
+ const usernameField = document.getElementById("username");
+ const tokenField = document.getElementById("token");
+
+ form.addEventListener("submit", event => {
+ event.preventDefault();
+
+ if (auth) {
+ alert("You are already authenticated!");
+ return;
+ }
+
+ const username = usernameField.value;
+ const token = tokenField.value;
+
+ if (!username) {
+ alert("Please provide a username!");
+ return;
+ }
+
+ auth = { USER: username, TOKEN: token };
+ fieldset.disabled = true;
+
+ connection.send(MessagePack.encode({
+ VERB: "STREAM",
+ PATH: ["user", username, "model"],
+ AUTH: auth,
+ META: {},
+ REID: 0,
+ PAYL: null,
+ }));
+ });
+}
+
+window.addEventListener("load", () => {
+ setUpConnection();
+ setUpFormListener();
+});
diff --git a/Resources/Views/index.leaf b/Resources/Views/index.leaf
index 515521d..e526468 100644
--- a/Resources/Views/index.leaf
+++ b/Resources/Views/index.leaf
@@ -1,9 +1,23 @@
- Lighthouse Test Server
+ Lighthouse Test
+
+
- Test
+
+ Lighthouse Test
+
+
+
+
+
From 162c10819bd7eda6c318fc56240dc51b5118a3e4 Mon Sep 17 00:00:00 2001
From: fwcd
Date: Mon, 21 Feb 2022 00:04:30 +0100
Subject: [PATCH 7/8] Render displays to test server's web interface
---
Public/scripts.js | 35 +++++++++++++++++++++++++++++++++--
Resources/Views/index.leaf | 2 +-
2 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/Public/scripts.js b/Public/scripts.js
index 6af2cca..035014a 100644
--- a/Public/scripts.js
+++ b/Public/scripts.js
@@ -1,5 +1,34 @@
+const lighthouseRows = 14;
+const lighthouseCols = 28;
+const xScale = 14;
+const yScale = 2 * xScale;
+
let connection = null;
let auth = null;
+let display = null;
+
+function updateDisplay(rgb) {
+ const ctx = display.getContext("2d");
+
+ for (let i = 0; i < (rgb.length / 3); i++) {
+ const r = rgb[3 * i];
+ const g = rgb[3 * i + 1];
+ const b = rgb[3 * i + 2];
+
+ const y = Math.floor(i / lighthouseCols);
+ const x = i % lighthouseCols;
+
+ ctx.fillStyle = `rgb(${r},${g},${b})`;
+ ctx.fillRect(x * xScale, y * yScale, xScale, yScale);
+ }
+}
+
+function setUpDisplay() {
+ display = document.getElementById("display");
+ display.width = xScale * lighthouseCols;
+ display.height = yScale * lighthouseRows;
+ updateDisplay(new Uint8Array(3 * lighthouseRows * lighthouseCols));
+}
function setUpConnection() {
connection = new WebSocket(`${location.origin.replace(/^http/, "ws")}/websocket`);
@@ -14,8 +43,9 @@ function setUpConnection() {
const message = MessagePack.decode(new Uint8Array(event.data));
if (message.PAYL instanceof Uint8Array) {
- const display = document.getElementById("display");
- display.innerText = message.PAYL.map(x => `${x}`).join(", ");
+ updateDisplay(message.PAYL);
+ } else {
+ console.log(`Something else: ${message.PAYL instanceof Uint8Array}`);
}
} catch (e) {
console.log(`Error while decoding message from WebSocket: ${e}`);
@@ -60,6 +90,7 @@ function setUpFormListener() {
}
window.addEventListener("load", () => {
+ setUpDisplay();
setUpConnection();
setUpFormListener();
});
diff --git a/Resources/Views/index.leaf b/Resources/Views/index.leaf
index e526468..79081e4 100644
--- a/Resources/Views/index.leaf
+++ b/Resources/Views/index.leaf
@@ -16,7 +16,7 @@
-
+