From 173cfb99d995515400614a1dfbb731f7cfb20802 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Fri, 20 Sep 2024 15:17:40 +0100 Subject: [PATCH 01/10] server sent events --- server-sent-events/.dockerignore | 2 + server-sent-events/.gitignore | 9 ++ server-sent-events/Dockerfile | 86 +++++++++++++++++++ server-sent-events/Package.swift | 36 ++++++++ server-sent-events/README.md | 2 + server-sent-events/Sources/App/App.swift | 27 ++++++ .../Sources/App/Application+build.swift | 81 +++++++++++++++++ .../Tests/AppTests/AppTests.swift | 24 ++++++ 8 files changed, 267 insertions(+) create mode 100644 server-sent-events/.dockerignore create mode 100644 server-sent-events/.gitignore create mode 100644 server-sent-events/Dockerfile create mode 100644 server-sent-events/Package.swift create mode 100644 server-sent-events/README.md create mode 100644 server-sent-events/Sources/App/App.swift create mode 100644 server-sent-events/Sources/App/Application+build.swift create mode 100644 server-sent-events/Tests/AppTests/AppTests.swift diff --git a/server-sent-events/.dockerignore b/server-sent-events/.dockerignore new file mode 100644 index 00000000..2fb33437 --- /dev/null +++ b/server-sent-events/.dockerignore @@ -0,0 +1,2 @@ +.build +.git \ No newline at end of file diff --git a/server-sent-events/.gitignore b/server-sent-events/.gitignore new file mode 100644 index 00000000..1cdfa95a --- /dev/null +++ b/server-sent-events/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/.swiftpm +/.devContainer +/Packages +/*.xcodeproj +xcuserdata/ +/.vscode/* +!/.vscode/hummingbird.code-snippets diff --git a/server-sent-events/Dockerfile b/server-sent-events/Dockerfile new file mode 100644 index 00000000..254212be --- /dev/null +++ b/server-sent-events/Dockerfile @@ -0,0 +1,86 @@ +# ================================ +# Build image +# ================================ +FROM swift:5.10-jammy as build + +# Install OS updates +RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ + && apt-get -q update \ + && apt-get -q dist-upgrade -y \ + && apt-get install -y libjemalloc-dev \ + && rm -rf /var/lib/apt/lists/* + +# Set up a build area +WORKDIR /build + +# First just resolve dependencies. +# This creates a cached layer that can be reused +# as long as your Package.swift/Package.resolved +# files do not change. +COPY ./Package.* ./ +RUN swift package resolve + +# Copy entire repo into container +COPY . . + +# Build everything, with optimizations, with static linking, and using jemalloc +RUN swift build -c release \ + --static-swift-stdlib \ + -Xlinker -ljemalloc + +# Switch to the staging area +WORKDIR /staging + +# Copy main executable to staging area +RUN cp "$(swift build --package-path /build -c release --show-bin-path)/App" ./ + +# Copy static swift backtracer binary to staging area +RUN cp "/usr/libexec/swift/linux/swift-backtrace-static" ./ + +# Copy resources bundled by SPM to staging area +RUN find -L "$(swift build --package-path /build -c release --show-bin-path)/" -regex '.*\.resources$' -exec cp -Ra {} ./ \; + +# Copy any resouces from the public directory and views directory if the directories exist +# Ensure that by default, neither the directory nor any of its contents are writable. +RUN [ -d /build/public ] && { mv /build/public ./public && chmod -R a-w ./public; } || true + +# ================================ +# Run image +# ================================ +FROM ubuntu:jammy + +# Make sure all system packages are up to date, and install only essential packages. +RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ + && apt-get -q update \ + && apt-get -q dist-upgrade -y \ + && apt-get -q install -y \ + libjemalloc2 \ + ca-certificates \ + tzdata \ +# If your app or its dependencies import FoundationNetworking, also install `libcurl4`. + # libcurl4 \ +# If your app or its dependencies import FoundationXML, also install `libxml2`. + # libxml2 \ + && rm -r /var/lib/apt/lists/* + +# Create a hummingbird user and group with /app as its home directory +RUN useradd --user-group --create-home --system --skel /dev/null --home-dir /app hummingbird + +# Switch to the new home directory +WORKDIR /app + +# Copy built executable and any staged resources from builder +COPY --from=build --chown=hummingbird:hummingbird /staging /app + +# Provide configuration needed by the built-in crash reporter and some sensible default behaviors. +ENV SWIFT_BACKTRACE=enable=yes,sanitize=yes,threads=all,images=all,interactive=no,swift-backtrace=./swift-backtrace-static + +# Ensure all further commands run as the hummingbird user +USER hummingbird:hummingbird + +# Let Docker bind to port 8080 +EXPOSE 8080 + +# Start the Hummingbird service when the image is run, default to listening on 8080 in production environment +ENTRYPOINT ["./App"] +CMD ["--hostname", "0.0.0.0", "--port", "8080"] diff --git a/server-sent-events/Package.swift b/server-sent-events/Package.swift new file mode 100644 index 00000000..b4f7a6f6 --- /dev/null +++ b/server-sent-events/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version:5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "server_sent_events", + platforms: [.macOS(.v14), .iOS(.v17), .tvOS(.v17)], + products: [ + .executable(name: "App", targets: ["App"]), + ], + dependencies: [ + .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"), + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), + .package(url: "https://github.com/adam-fowler/SSEKit.git", branch: "public-makebuffer"), + ], + targets: [ + .executableTarget( + name: "App", + dependencies: [ + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "Hummingbird", package: "hummingbird"), + .product(name: "SSEKit", package: "SSEKit"), + ], + path: "Sources/App" + ), + .testTarget( + name: "AppTests", + dependencies: [ + .byName(name: "App"), + .product(name: "HummingbirdTesting", package: "hummingbird"), + ], + path: "Tests/AppTests" + ), + ] +) diff --git a/server-sent-events/README.md b/server-sent-events/README.md new file mode 100644 index 00000000..4235aad7 --- /dev/null +++ b/server-sent-events/README.md @@ -0,0 +1,2 @@ +# server_side_events +Hummingbird server framework project diff --git a/server-sent-events/Sources/App/App.swift b/server-sent-events/Sources/App/App.swift new file mode 100644 index 00000000..04f47bb4 --- /dev/null +++ b/server-sent-events/Sources/App/App.swift @@ -0,0 +1,27 @@ +import ArgumentParser +import Hummingbird +import Logging + +@main +struct App: AsyncParsableCommand, AppArguments { + @Option(name: .shortAndLong) + var hostname: String = "127.0.0.1" + + @Option(name: .shortAndLong) + var port: Int = 8080 + + @Option(name: .shortAndLong) + var logLevel: Logger.Level? + + func run() async throws { + let app = try await buildApplication(self) + try await app.runService() + } +} + +/// Extend `Logger.Level` so it can be used as an argument +#if hasFeature(RetroactiveAttribute) + extension Logger.Level: @retroactive ExpressibleByArgument {} +#else + extension Logger.Level: ExpressibleByArgument {} +#endif diff --git a/server-sent-events/Sources/App/Application+build.swift b/server-sent-events/Sources/App/Application+build.swift new file mode 100644 index 00000000..f8a8226a --- /dev/null +++ b/server-sent-events/Sources/App/Application+build.swift @@ -0,0 +1,81 @@ +import Hummingbird +import Logging +import NIOCore +import SSEKit + +/// Application arguments protocol. We use a protocol so we can call +/// `buildApplication` inside Tests as well as in the App executable. +/// Any variables added here also have to be added to `App` in App.swift and +/// `TestArguments` in AppTest.swift +public protocol AppArguments { + var hostname: String { get } + var port: Int { get } + var logLevel: Logger.Level? { get } +} + +// Request context used by application +struct AppRequestContext: RequestContext { + var coreContext: CoreRequestContextStorage + let channel: Channel + + init(source: Source) { + self.coreContext = .init(source: source) + self.channel = source.channel + } +} + +/// Build application +/// - Parameter arguments: application arguments +public func buildApplication(_ arguments: some AppArguments) async throws + -> some ApplicationProtocol +{ + let environment = Environment() + let logger = { + var logger = Logger(label: "server_side_events") + logger.logLevel = + arguments.logLevel ?? environment.get("LOG_LEVEL").map { + Logger.Level(rawValue: $0) ?? .info + } ?? .info + return logger + }() + let router = buildRouter() + let app = Application( + router: router, + configuration: .init( + address: .hostname(arguments.hostname, port: arguments.port), + serverName: "server_sent_events" + ), + logger: logger + ) + return app +} + +/// Build router +func buildRouter() -> Router { + let router = Router(context: AppRequestContext.self) + // Add middleware + router.addMiddleware { + // logging middleware + LogRequestsMiddleware(.info) + } + // Add health endpoint + router.get("/health") { _, _ -> HTTPResponse.Status in + return .ok + } + router.get("events") { _, context -> Response in + return .init( + status: .ok, headers: [.contentType: "text/event-stream"], + body: .init { writer in + let allocator = ByteBufferAllocator() + for value in 0..<250 { + try await Task.sleep(for: .seconds(10)) + try await writer.write( + ServerSentEvent(data: .init(string: value.description)).makeBuffer( + allocator: allocator)) + } + try await writer.finish(nil) + } + ) + } + return router +} diff --git a/server-sent-events/Tests/AppTests/AppTests.swift b/server-sent-events/Tests/AppTests/AppTests.swift new file mode 100644 index 00000000..6ae7eff7 --- /dev/null +++ b/server-sent-events/Tests/AppTests/AppTests.swift @@ -0,0 +1,24 @@ +import Hummingbird +import HummingbirdTesting +import Logging +import XCTest + +@testable import App + +final class AppTests: XCTestCase { + struct TestArguments: AppArguments { + let hostname = "127.0.0.1" + let port = 0 + let logLevel: Logger.Level? = .trace + } + + func testApp() async throws { + let args = TestArguments() + let app = try await buildApplication(args) + try await app.test(.router) { client in + try await client.execute(uri: "/health", method: .get) { response in + XCTAssertEqual(response.status, .ok) + } + } + } +} From 038a0c14e0d7f9a31663062ca339fb75111a0545 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Fri, 6 Dec 2024 10:02:10 +0000 Subject: [PATCH 02/10] Add .index-build/ --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 50762abd..69b07027 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store .build/ +.index-build/ .vscode/ .index-build/ /Packages From bd7bfc2d22e40a7861541132c47fc99470959bdf Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 8 Dec 2024 12:12:19 +0000 Subject: [PATCH 03/10] Use new consumeWithInboundCloseHandler, publish requests to event stream --- server-sent-events/Package.swift | 13 +++-- .../Sources/App/Application+build.swift | 53 +++++++++++++---- .../Sources/App/Publisher.swift | 58 +++++++++++++++++++ 3 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 server-sent-events/Sources/App/Publisher.swift diff --git a/server-sent-events/Package.swift b/server-sent-events/Package.swift index b4f7a6f6..13106b53 100644 --- a/server-sent-events/Package.swift +++ b/server-sent-events/Package.swift @@ -1,18 +1,21 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "server_sent_events", - platforms: [.macOS(.v14), .iOS(.v17), .tvOS(.v17)], + platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)], products: [ - .executable(name: "App", targets: ["App"]), + .executable(name: "App", targets: ["App"]) ], dependencies: [ - .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"), + .package( + url: "https://github.com/hummingbird-project/hummingbird.git", + branch: "cancel-on-inbound-close2" + ), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), - .package(url: "https://github.com/adam-fowler/SSEKit.git", branch: "public-makebuffer"), + .package(url: "https://github.com/orlandos-nl/SSEKit.git", branch: "main"), ], targets: [ .executableTarget( diff --git a/server-sent-events/Sources/App/Application+build.swift b/server-sent-events/Sources/App/Application+build.swift index f8a8226a..53388204 100644 --- a/server-sent-events/Sources/App/Application+build.swift +++ b/server-sent-events/Sources/App/Application+build.swift @@ -26,7 +26,9 @@ struct AppRequestContext: RequestContext { /// Build application /// - Parameter arguments: application arguments -public func buildApplication(_ arguments: some AppArguments) async throws +public func buildApplication( + _ arguments: some AppArguments +) async throws -> some ApplicationProtocol { let environment = Environment() @@ -38,20 +40,22 @@ public func buildApplication(_ arguments: some AppArguments) async throws } ?? .info return logger }() - let router = buildRouter() + let requestPublisher = Publisher() + let router = buildRouter(requestPublisher: requestPublisher) let app = Application( router: router, configuration: .init( address: .hostname(arguments.hostname, port: arguments.port), serverName: "server_sent_events" ), + services: [requestPublisher], logger: logger ) return app } /// Build router -func buildRouter() -> Router { +func buildRouter(requestPublisher: Publisher) -> Router { let router = Router(context: AppRequestContext.self) // Add middleware router.addMiddleware { @@ -60,22 +64,47 @@ func buildRouter() -> Router { } // Add health endpoint router.get("/health") { _, _ -> HTTPResponse.Status in - return .ok + .ok } - router.get("events") { _, context -> Response in - return .init( - status: .ok, headers: [.contentType: "text/event-stream"], + router.get("events") { request, context -> Response in + .init( + status: .ok, + headers: [.contentType: "text/event-stream"], body: .init { writer in let allocator = ByteBufferAllocator() - for value in 0..<250 { - try await Task.sleep(for: .seconds(10)) - try await writer.write( - ServerSentEvent(data: .init(string: value.description)).makeBuffer( - allocator: allocator)) + let (stream, id) = requestPublisher.addSubsciber() + var unsafeWriter = UnsafeTransfer(writer) + try await request.body.consumeWithInboundCloseHandler { request in + for try await value in stream { + try await unsafeWriter.wrappedValue.write( + ServerSentEvent(data: .init(string: value)).makeBuffer( + allocator: allocator + ) + ) + } + } onInboundClosed: { + requestPublisher.removeSubsciber(id) } try await writer.finish(nil) } ) } + router.get("**") { request, _ -> HTTPResponse.Status in + await requestPublisher.publish("\(request.method): \(request.uri.path)") + return .ok + } return router } + +@usableFromInline +package struct UnsafeTransfer { + @usableFromInline + package var wrappedValue: Wrapped + + @inlinable + package init(_ wrappedValue: Wrapped) { + self.wrappedValue = wrappedValue + } +} + +extension UnsafeTransfer: @unchecked Sendable {} diff --git a/server-sent-events/Sources/App/Publisher.swift b/server-sent-events/Sources/App/Publisher.swift new file mode 100644 index 00000000..e005d1d4 --- /dev/null +++ b/server-sent-events/Sources/App/Publisher.swift @@ -0,0 +1,58 @@ +import Foundation +import ServiceLifecycle + +actor Publisher: Service { + typealias SubscriptionID = UUID + enum SubscriptionCommand { + case add(SubscriptionID, AsyncStream.Continuation) + case remove(SubscriptionID) + } + nonisolated let (subStream, subSource) = AsyncStream.makeStream() + + init() { + self.subscriptions = [:] + } + + func publish(_ value: Value) async { + for subscription in self.subscriptions.values { + subscription.yield(value) + } + } + + nonisolated func addSubsciber() -> (AsyncStream, SubscriptionID) { + let id = SubscriptionID() + let (stream, source) = AsyncStream.makeStream() + subSource.yield(.add(id, source)) + return (stream, id) + } + + nonisolated func removeSubsciber(_ id: SubscriptionID) { + subSource.yield(.remove(id)) + } + + func run() async throws { + try await withGracefulShutdownHandler { + for try await command in self.subStream { + switch command { + case .add(let id, let source): + await self._addSubsciber(id, source: source) + case .remove(let id): + self._removeSubsciber(id) + } + } + } onGracefulShutdown: { + self.subSource.finish() + } + } + + private func _addSubsciber(_ id: SubscriptionID, source: AsyncStream.Continuation) async { + self.subscriptions[id] = source + } + + private func _removeSubsciber(_ id: SubscriptionID) { + self.subscriptions[id]?.finish() + self.subscriptions[id] = nil + } + + var subscriptions: [UUID: AsyncStream.Continuation] +} From 1a5c2c5fd1640d6a8d7a20dca458847a14025a17 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Mon, 9 Dec 2024 15:58:29 +0000 Subject: [PATCH 04/10] Change to pubishing all requests to server --- .swift-format | 63 +++++++++++++++++++ server-sent-events/README.md | 8 ++- .../Sources/App/Application+build.swift | 50 +++++++-------- .../Sources/App/Publisher.swift | 16 +++-- 4 files changed, 103 insertions(+), 34 deletions(-) create mode 100644 .swift-format diff --git a/.swift-format b/.swift-format new file mode 100644 index 00000000..bb3dcff3 --- /dev/null +++ b/.swift-format @@ -0,0 +1,63 @@ +{ + "version" : 1, + "indentation" : { + "spaces" : 4 + }, + "tabWidth" : 4, + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "spacesAroundRangeFormationOperators" : false, + "indentConditionalCompilationBlocks" : false, + "indentSwitchCaseLabels" : false, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : true, + "lineBreakBeforeEachGenericRequirement" : true, + "lineLength" : 150, + "maximumBlankLines" : 1, + "respectsExistingLineBreaks" : true, + "prioritizeKeepingFunctionOutputTogether" : true, + "multiElementCollectionTrailingCommas" : true, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLiteralForEmptyCollectionInit" : false, + "AlwaysUseLowerCamelCase" : false, + "AmbiguousTrailingClosureOverload" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : false, + "NeverUseForceTry" : false, + "NeverUseImplicitlyUnwrappedOptionals" : false, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : true, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "UseEarlyExits" : false, + "UseExplicitNilCheckInConditions" : false, + "UseLetInEveryBoundCaseVariable" : false, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : false, + "UseSynthesizedInitializer" : false, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : false + } +} diff --git a/server-sent-events/README.md b/server-sent-events/README.md index 4235aad7..d8d4b980 100644 --- a/server-sent-events/README.md +++ b/server-sent-events/README.md @@ -1,2 +1,6 @@ -# server_side_events -Hummingbird server framework project +# Server Send Events + +Example demonstrating how to setup a route returning Server Sent Events. + +The application has one route. `GET /events`. If you call this then every other request sent to the server will be reported back to this route as a server sent event. + diff --git a/server-sent-events/Sources/App/Application+build.swift b/server-sent-events/Sources/App/Application+build.swift index 53388204..b1708df0 100644 --- a/server-sent-events/Sources/App/Application+build.swift +++ b/server-sent-events/Sources/App/Application+build.swift @@ -2,6 +2,7 @@ import Hummingbird import Logging import NIOCore import SSEKit +import ServiceLifecycle /// Application arguments protocol. We use a protocol so we can call /// `buildApplication` inside Tests as well as in the App executable. @@ -61,10 +62,7 @@ func buildRouter(requestPublisher: Publisher) -> Router HTTPResponse.Status in - .ok + PublishRequestsMiddleware(requestPublisher: requestPublisher) } router.get("events") { request, context -> Response in .init( @@ -72,39 +70,35 @@ func buildRouter(requestPublisher: Publisher) -> Router HTTPResponse.Status in - await requestPublisher.publish("\(request.method): \(request.uri.path)") - return .ok - } return router } -@usableFromInline -package struct UnsafeTransfer { - @usableFromInline - package var wrappedValue: Wrapped - - @inlinable - package init(_ wrappedValue: Wrapped) { - self.wrappedValue = wrappedValue +/// Middleware to publish requests +struct PublishRequestsMiddleware: RouterMiddleware { + let requestPublisher: Publisher + func handle(_ request: Request, context: Context, next: (Request, Context) async throws -> Response) async throws -> Response { + await requestPublisher.publish("\(request.method): \(request.uri.path)") + return try await next(request, context) } } - -extension UnsafeTransfer: @unchecked Sendable {} diff --git a/server-sent-events/Sources/App/Publisher.swift b/server-sent-events/Sources/App/Publisher.swift index e005d1d4..20d4f4ac 100644 --- a/server-sent-events/Sources/App/Publisher.swift +++ b/server-sent-events/Sources/App/Publisher.swift @@ -1,6 +1,7 @@ import Foundation import ServiceLifecycle +/// Basic PUB/SUB service. actor Publisher: Service { typealias SubscriptionID = UUID enum SubscriptionCommand { @@ -13,29 +14,36 @@ actor Publisher: Service { self.subscriptions = [:] } + /// Publish to service + /// - Parameter value: Value being published func publish(_ value: Value) async { for subscription in self.subscriptions.values { subscription.yield(value) } } - nonisolated func addSubsciber() -> (AsyncStream, SubscriptionID) { + /// Subscribe to service + /// - Returns: AsyncStream of values, and subscription identifier + nonisolated func subscribe() -> (AsyncStream, SubscriptionID) { let id = SubscriptionID() let (stream, source) = AsyncStream.makeStream() subSource.yield(.add(id, source)) return (stream, id) } - nonisolated func removeSubsciber(_ id: SubscriptionID) { + /// Unsubscribe from service + /// - Parameter id: Subscription identifier + nonisolated func unsubscribe(_ id: SubscriptionID) { subSource.yield(.remove(id)) } + /// Service run function func run() async throws { try await withGracefulShutdownHandler { for try await command in self.subStream { switch command { case .add(let id, let source): - await self._addSubsciber(id, source: source) + self._addSubsciber(id, source: source) case .remove(let id): self._removeSubsciber(id) } @@ -45,7 +53,7 @@ actor Publisher: Service { } } - private func _addSubsciber(_ id: SubscriptionID, source: AsyncStream.Continuation) async { + private func _addSubsciber(_ id: SubscriptionID, source: AsyncStream.Continuation) { self.subscriptions[id] = source } From 09c9b179c653b0abcdf2db3b781e42d767de4f4b Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Mon, 9 Dec 2024 20:11:35 +0000 Subject: [PATCH 05/10] Update Package.swift --- server-sent-events/Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-sent-events/Package.swift b/server-sent-events/Package.swift index 13106b53..ecefaa62 100644 --- a/server-sent-events/Package.swift +++ b/server-sent-events/Package.swift @@ -15,7 +15,7 @@ let package = Package( branch: "cancel-on-inbound-close2" ), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), - .package(url: "https://github.com/orlandos-nl/SSEKit.git", branch: "main"), + .package(url: "https://github.com/orlandos-nl/SSEKit.git", from: "1.1.0"), ], targets: [ .executableTarget( From 87d8cbfe9a653bb792982a955065c9ce7fea172b Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Tue, 10 Dec 2024 07:27:44 +0000 Subject: [PATCH 06/10] Remove test --- server-sent-events/Tests/AppTests/AppTests.swift | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/server-sent-events/Tests/AppTests/AppTests.swift b/server-sent-events/Tests/AppTests/AppTests.swift index 6ae7eff7..8489019f 100644 --- a/server-sent-events/Tests/AppTests/AppTests.swift +++ b/server-sent-events/Tests/AppTests/AppTests.swift @@ -11,14 +11,4 @@ final class AppTests: XCTestCase { let port = 0 let logLevel: Logger.Level? = .trace } - - func testApp() async throws { - let args = TestArguments() - let app = try await buildApplication(args) - try await app.test(.router) { client in - try await client.execute(uri: "/health", method: .get) { response in - XCTAssertEqual(response.status, .ok) - } - } - } } From afd07a7035bf71304312087b48d6c78f444950f2 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Thu, 12 Dec 2024 09:36:42 +0000 Subject: [PATCH 07/10] Docker uses jammy 6.0 --- server-sent-events/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-sent-events/Dockerfile b/server-sent-events/Dockerfile index 254212be..cdc8c427 100644 --- a/server-sent-events/Dockerfile +++ b/server-sent-events/Dockerfile @@ -1,7 +1,7 @@ # ================================ # Build image # ================================ -FROM swift:5.10-jammy as build +FROM swift:6.0-jammy as build # Install OS updates RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \ From 666d7df2a22adc4a1a4fd536a598725548d4017a Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Thu, 12 Dec 2024 10:19:40 +0000 Subject: [PATCH 08/10] Add server-sent-events to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6a085d14..cc97fcf5 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Examples converted to Hummingbird 2.0 - [proxy-server](https://github.com/hummingbird-project/hummingbird-examples/tree/main/proxy-server) - Using AsyncHTTPClient to build a proxy server - [response-body-processing](https://github.com/hummingbird-project/hummingbird-examples/tree/main/response-body-processing) - Example showing how to process a response body in middleware. - [s3-file-provider](https://github.com/hummingbird-project/hummingbird-examples/tree/main/s3-file-provider) - Use a custom FileProvider to serve files from S3 with FileMiddleware. +- [server-send-events](https://github.com/hummingbird-project/hummingbird-examples/tree/main/server-send-events) - Server sent events. - [sessions](https://github.com/hummingbird-project/hummingbird-examples/tree/main/sessions) - Username/password and session authentication. - [todos-dynamodb](https://github.com/hummingbird-project/hummingbird-examples/tree/main/todos-dynamodb) - Todos application, based off [TodoBackend](http://todobackend.com) spec, using DynamoDB. - [todos-fluent](https://github.com/hummingbird-project/hummingbird-examples/tree/1.x.x/todos-fluent) - Todos application, based off [TodoBackend](http://todobackend.com) spec, using Fluent From 5b268aa4d74b9748a23f6396fea7f182692c749c Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 28 Dec 2024 11:26:41 +0000 Subject: [PATCH 09/10] Use HB 2.6.0 --- server-sent-events/Package.swift | 5 +---- server-sent-events/Sources/App/Application+build.swift | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/server-sent-events/Package.swift b/server-sent-events/Package.swift index ecefaa62..18427e91 100644 --- a/server-sent-events/Package.swift +++ b/server-sent-events/Package.swift @@ -10,10 +10,7 @@ let package = Package( .executable(name: "App", targets: ["App"]) ], dependencies: [ - .package( - url: "https://github.com/hummingbird-project/hummingbird.git", - branch: "cancel-on-inbound-close2" - ), + .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.6.0"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), .package(url: "https://github.com/orlandos-nl/SSEKit.git", from: "1.1.0"), ], diff --git a/server-sent-events/Sources/App/Application+build.swift b/server-sent-events/Sources/App/Application+build.swift index b1708df0..45271f32 100644 --- a/server-sent-events/Sources/App/Application+build.swift +++ b/server-sent-events/Sources/App/Application+build.swift @@ -73,7 +73,7 @@ func buildRouter(requestPublisher: Publisher) -> Router Date: Sat, 28 Dec 2024 11:28:30 +0000 Subject: [PATCH 10/10] Remove symlinks that shouldnt be there --- auth-cognito/Packages/hummingbird | 1 - todos-auth-fluent/Packages/hummingbird-auth | 1 - 2 files changed, 2 deletions(-) delete mode 160000 auth-cognito/Packages/hummingbird delete mode 120000 todos-auth-fluent/Packages/hummingbird-auth diff --git a/auth-cognito/Packages/hummingbird b/auth-cognito/Packages/hummingbird deleted file mode 160000 index 195aeefa..00000000 --- a/auth-cognito/Packages/hummingbird +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 195aeefa7c4b899ab8f338daf18fb42df6b2af01 diff --git a/todos-auth-fluent/Packages/hummingbird-auth b/todos-auth-fluent/Packages/hummingbird-auth deleted file mode 120000 index 0d7279b8..00000000 --- a/todos-auth-fluent/Packages/hummingbird-auth +++ /dev/null @@ -1 +0,0 @@ -/Users/adamfowler/Developer/server/hummingbird-project/hummingbird-auth \ No newline at end of file