Skip to content

Commit

Permalink
Enable StrictConcurrency
Browse files Browse the repository at this point in the history
  • Loading branch information
bok- committed Jul 20, 2024
1 parent f97f8b2 commit 35ce395
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 98 deletions.
6 changes: 6 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,19 @@ let package = Package(
.product(name: "HummingbirdCore", package: "hummingbird"),
.product(name: "HummingbirdHTTP2", package: "hummingbird"),
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency"),
]
),
.testTarget(
name: "CompatibilityTests",
dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.target(name: "HummingbirdGRPC"),
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency"),
]
),
]
Expand Down
6 changes: 3 additions & 3 deletions Sources/HummingbirdGRPC/CallHandlerProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@

import GRPC

final class HBCallHandlerProvider: CallHandlerProvider {
final class HBCallHandlerProvider: CallHandlerProvider & Sendable {

// MARK: - Properties

let serviceName: Substring
let method: Substring
let handlerFactory: (CallHandlerContext) -> GRPCServerHandlerProtocol
let handlerFactory: @Sendable (CallHandlerContext) -> GRPCServerHandlerProtocol


// MARK: - Initialisation

init(serviceName: String, method: String, handlerFactory: @escaping (CallHandlerContext) -> GRPCServerHandlerProtocol) {
init(serviceName: String, method: String, handlerFactory: @escaping @Sendable (CallHandlerContext) -> GRPCServerHandlerProtocol) {
self.serviceName = serviceName[...]
self.method = method[...]
self.handlerFactory = handlerFactory
Expand Down
10 changes: 4 additions & 6 deletions Sources/HummingbirdGRPC/GRPCNegotiationChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public struct GRPCNegotiationChannel: HTTPChannelHandler {

// MARK: - Properties

private let services: @Sendable () -> [CallHandlerProvider]
private let serverBuilder: GRPCServerBuilder
private let grpcConfiguration: HTTPServerBuilder.GRPCConfiguration
private let sslContext: NIOSSLContext
private let additionalChannelHandlers: @Sendable () -> [any RemovableChannelHandler]
Expand All @@ -37,13 +37,13 @@ public struct GRPCNegotiationChannel: HTTPChannelHandler {
// MARK: - Initialisation

init(
services: @escaping @Sendable () -> [CallHandlerProvider],
serverBuilder: GRPCServerBuilder,
grpcConfiguration: HTTPServerBuilder.GRPCConfiguration,
sslContext: NIOSSLContext,
additionalChannelHandlers: @escaping @Sendable () -> [any RemovableChannelHandler] = { [] },
responder: @escaping HTTPChannelHandler.Responder
) {
self.services = services
self.serverBuilder = serverBuilder
self.grpcConfiguration = grpcConfiguration
self.sslContext = sslContext
self.additionalChannelHandlers = additionalChannelHandlers
Expand Down Expand Up @@ -155,9 +155,7 @@ public struct GRPCNegotiationChannel: HTTPChannelHandler {

private func makeGRPCChannelHandler(logger: Logger) -> some ChannelInboundHandler {
HTTP2ToRawGRPCServerCodec(
servicesByName: services().reduce(into: [Substring: CallHandlerProvider]()) { result, provider in
result[provider.serviceName] = provider
},
servicesByName: serverBuilder.servicesByName(),
encoding: grpcConfiguration.encoding,
errorDelegate: grpcConfiguration.errorDelegate,
normalizeHeaders: grpcConfiguration.normalizeHeaders,
Expand Down
8 changes: 4 additions & 4 deletions Sources/HummingbirdGRPC/GRPCServerBuilder+NIORoutes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public extension GRPCServerBuilder {
requestType: Request.Type = Request.self,
responseType: Response.Type = Response.self,
interceptors: [ServerInterceptor<Request, Response>] = [],
handler: @escaping (Request, StatusOnlyCallContext) -> EventLoopFuture<Response>
handler: @escaping @Sendable (Request, StatusOnlyCallContext) -> EventLoopFuture<Response>
) -> GRPCServerBuilder where Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message {
let provider = HBCallHandlerProvider(serviceName: serviceName, method: method) { context in
UnaryServerHandler(
Expand Down Expand Up @@ -105,7 +105,7 @@ public extension GRPCServerBuilder {
requestType: Request.Type = Request.self,
responseType: Response.Type = Response.self,
interceptors: [ServerInterceptor<Request, Response>] = [],
handler: @escaping (Request, StreamingResponseCallContext<Response>) -> EventLoopFuture<GRPCStatus>
handler: @escaping @Sendable (Request, StreamingResponseCallContext<Response>) -> EventLoopFuture<GRPCStatus>
) -> GRPCServerBuilder where Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message {
let provider = HBCallHandlerProvider(serviceName: serviceName, method: method) { context in
ServerStreamingServerHandler(
Expand Down Expand Up @@ -165,7 +165,7 @@ public extension GRPCServerBuilder {
requestType: Request.Type = Request.self,
responseType: Response.Type = Response.self,
interceptors: [ServerInterceptor<Request, Response>] = [],
handler: @escaping (UnaryResponseCallContext<Response>) -> EventLoopFuture<(StreamEvent<Request>) -> Void>
handler: @escaping @Sendable (UnaryResponseCallContext<Response>) -> EventLoopFuture<(StreamEvent<Request>) -> Void>
) -> GRPCServerBuilder where Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message {
let provider = HBCallHandlerProvider(serviceName: serviceName, method: method) { context in
ClientStreamingServerHandler(
Expand Down Expand Up @@ -226,7 +226,7 @@ public extension GRPCServerBuilder {
requestType: Request.Type = Request.self,
responseType: Response.Type = Response.self,
interceptors: [ServerInterceptor<Request, Response>] = [],
handler: @escaping (StreamingResponseCallContext<Response>) -> EventLoopFuture<(StreamEvent<Request>) -> Void>
handler: @escaping @Sendable (StreamingResponseCallContext<Response>) -> EventLoopFuture<(StreamEvent<Request>) -> Void>
) -> GRPCServerBuilder where Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message {
let provider = HBCallHandlerProvider(serviceName: serviceName, method: method) { context in
BidirectionalStreamingServerHandler(
Expand Down
27 changes: 21 additions & 6 deletions Sources/HummingbirdGRPC/GRPCServerBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ import GRPC
import Hummingbird
import HummingbirdCore
import HummingbirdHTTP2
import NIOConcurrencyHelpers
import NIOSSL
import SwiftProtobuf

/// A builder type that supports the setup and configuration of grpc-swift support.
public final class GRPCServerBuilder {
public final class GRPCServerBuilder: Sendable {

var serviceProviders = [any CallHandlerProvider]()
public typealias SendableCallHandlerProvider = CallHandlerProvider & Sendable

let serviceProviders = NIOLockedValueBox([any SendableCallHandlerProvider]())


// MARK: - Initialsiation
Expand All @@ -31,15 +34,27 @@ public final class GRPCServerBuilder {
// MARK: - Services Providers

@discardableResult
public func addServiceProvider(_ provider: CallHandlerProvider) -> GRPCServerBuilder {
serviceProviders.append(provider)
public func addServiceProvider(_ provider: any SendableCallHandlerProvider) -> GRPCServerBuilder {
serviceProviders.withLockedValue {
$0.append(provider)
}
return self
}

@discardableResult
public func addServiceProviders(_ providers: [CallHandlerProvider]) -> GRPCServerBuilder {
serviceProviders.append(contentsOf: providers)
public func addServiceProviders(_ providers: [any SendableCallHandlerProvider]) -> GRPCServerBuilder {
serviceProviders.withLockedValue {
$0.append(contentsOf: providers)
}
return self
}

public func servicesByName() -> [Substring: CallHandlerProvider] {
serviceProviders.withLockedValue {
$0.reduce(into: [Substring: CallHandlerProvider]()) { result, provider in
result[provider.serviceName] = provider
}
}
}

}
2 changes: 1 addition & 1 deletion Sources/HummingbirdGRPC/HTTPServerBuilder+GRPC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public extension HTTPServerBuilder {

return .init { responder in
GRPCNegotiationChannel(
services: { serverBuilder.serviceProviders },
serverBuilder: serverBuilder,
grpcConfiguration: grpcConfiguration,
sslContext: sslContext,
responder: responder
Expand Down
2 changes: 1 addition & 1 deletion Tests/CompatibilityTests/Echo/EchoProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import GRPC
import NIOCore
import SwiftProtobuf

public class EchoProvider: Echo_EchoProvider {
public final class EchoProvider: Echo_EchoProvider & Sendable {
public let interceptors: Echo_EchoServerInterceptorFactoryProtocol?

public init(interceptors: Echo_EchoServerInterceptorFactoryProtocol? = nil) {
Expand Down
16 changes: 8 additions & 8 deletions Tests/CompatibilityTests/GRPCAsyncGeneratedCodeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ final class GRPCAsyncGeneratedCodeTests: ServerTestCase {
func testUnaryCall() async throws {

// GIVEN an HBApplication with gRPC support
let app = try startServer(port: 9001)
let app = try await startServer(port: 9001)

// AND GIVEN a grpc-swift client
let client = makeAsyncGRPCClient(app: app, port: 9001)
let client = try await makeAsyncGRPCClient(app: app, port: 9001)

// AND GIVEN a promise that the response will be received
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
Expand All @@ -45,10 +45,10 @@ final class GRPCAsyncGeneratedCodeTests: ServerTestCase {
func testServerStreamingCall() async throws {

// GIVEN an HBApplication with gRPC support
let app = try startServer(port: 9002)
let app = try await startServer(port: 9002)

// AND GIVEN a grpc-swift client
let client = makeAsyncGRPCClient(app: app, port: 9002)
let client = try await makeAsyncGRPCClient(app: app, port: 9002)

// AND GIVEN a promise that the response will be received
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
Expand All @@ -71,10 +71,10 @@ final class GRPCAsyncGeneratedCodeTests: ServerTestCase {
func testClientStreamingCall() async throws {

// GIVEN an HBApplication with gRPC support
let app = try startServer(port: 9003)
let app = try await startServer(port: 9003)

// AND GIVEN a grpc-swift client
let client = makeAsyncGRPCClient(app: app, port: 9003)
let client = try await makeAsyncGRPCClient(app: app, port: 9003)

// AND GIVEN a promise that the response will be received
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
Expand All @@ -97,10 +97,10 @@ final class GRPCAsyncGeneratedCodeTests: ServerTestCase {
func testBidirectionalStreamingCall() async throws {

// GIVEN an HBApplication with gRPC support
let app = try startServer(port: 9004)
let app = try await startServer(port: 9004)

// AND GIVEN a grpc-swift client
let client = makeAsyncGRPCClient(app: app, port: 9004)
let client = try await makeAsyncGRPCClient(app: app, port: 9004)

// AND GIVEN a promise that the response will be received
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
Expand Down
16 changes: 8 additions & 8 deletions Tests/CompatibilityTests/GRPCAsyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ final class GRPCMethodAsyncTests: ServerTestCase {
func testUnaryCall() async throws {

// GIVEN an HBApplication with gRPC support and a route that echos the input
let app = try startServer(port: 9010, serverBuilder: {
let app = try await startServer(port: 9010, serverBuilder: {
$0.onUnary("echo.Echo", "Get", requestType: Echo_EchoRequest.self) { request, context async in
Echo_EchoResponse.with {
$0.text = "Swift echo get: " + request.text
Expand All @@ -36,7 +36,7 @@ final class GRPCMethodAsyncTests: ServerTestCase {
})

// AND GIVEN a grpc-swift client
let client = makeAsyncGRPCClient(app: app, port: 9010)
let client = try await makeAsyncGRPCClient(app: app, port: 9010)

// AND GIVEN a promise that the response will be received
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
Expand All @@ -54,7 +54,7 @@ final class GRPCMethodAsyncTests: ServerTestCase {
func testServerStreamingCall() async throws {

// GIVEN an HBApplication with gRPC support and a route that expands the input
let app = try startServer(port: 9011, serverBuilder: {
let app = try await startServer(port: 9011, serverBuilder: {
$0.onServerStream("echo.Echo", "Expand", requestType: Echo_EchoRequest.self) { request, responseStream, _ async throws in
for (i, part) in request.text.components(separatedBy: " ").lazy.enumerated() {
try await responseStream.send(Echo_EchoResponse.with { $0.text = "Swift echo expand (\(i)): \(part)" })
Expand All @@ -63,7 +63,7 @@ final class GRPCMethodAsyncTests: ServerTestCase {
})

// AND GIVEN a grpc-swift client
let client = makeAsyncGRPCClient(app: app, port: 9011)
let client = try await makeAsyncGRPCClient(app: app, port: 9011)

// AND GIVEN a promise that the response will be received
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
Expand All @@ -84,7 +84,7 @@ final class GRPCMethodAsyncTests: ServerTestCase {
func testClientStreamingCall() async throws {

// GIVEN an HBApplication with gRPC support and a route that collects the input
let app = try startServer(port: 9012, serverBuilder: {
let app = try await startServer(port: 9012, serverBuilder: {
$0.onClientStream("echo.Echo", "Collect", requestType: Echo_EchoRequest.self) { requestStream, context async throws in
let text = try await requestStream.reduce(into: "Swift echo collect:") { result, request in
result += " \(request.text)"
Expand All @@ -95,7 +95,7 @@ final class GRPCMethodAsyncTests: ServerTestCase {
})

// AND GIVEN a grpc-swift client
let client = makeAsyncGRPCClient(app: app, port: 9012)
let client = try await makeAsyncGRPCClient(app: app, port: 9012)

// AND GIVEN a promise that the response will be received
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
Expand All @@ -118,7 +118,7 @@ final class GRPCMethodAsyncTests: ServerTestCase {
func testBidirectionalStreamingCall() async throws {

// GIVEN an HBApplication with gRPC support and a route that echos the input
let app = try startServer(port: 9013, serverBuilder: {
let app = try await startServer(port: 9013, serverBuilder: {
$0.onBidirectionalStream("echo.Echo", "Update", requestType: Echo_EchoRequest.self) { requestStream, responseStream, _ async throws in
var counter = 0
for try await request in requestStream {
Expand All @@ -130,7 +130,7 @@ final class GRPCMethodAsyncTests: ServerTestCase {
})

// AND GIVEN a grpc-swift client
let client = makeAsyncGRPCClient(app: app, port: 9013)
let client = try await makeAsyncGRPCClient(app: app, port: 9013)

// AND GIVEN a promise that the response will be received
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
Expand Down
Loading

0 comments on commit 35ce395

Please sign in to comment.