我正在尝试使用 NIO 创建一个 MockHTTPServer。试图使其兼容 swift 6。我一直在试图弄清楚如何解决 MockHTTPHandler 中发生的
Call to actor-isolated instance method 'getResponse(for:)' in a synchronous nonisolated context
问题。我希望有人能告诉我这很简单。将有问题的代码包装在任务中会产生不同的错误:“‘() async -> ()’类型的任务隔离值作为强传输参数传递;以后的访问可能会发生争用”,我无法弄清楚哪个函数调用正在比赛。
fileprivate actor MockHTTPServer {
private let group: EventLoopGroup
private let channel: Channel
let port: Int
private var responses: [String: (HTTPResponseStatus, String)] = [:]
private init(group: EventLoopGroup, channel: Channel) {
self.group = group
self.channel = channel
self.port = channel.localAddress?.port ?? -1
}
static func start(group: EventLoopGroup) async throws -> MockHTTPServer {
let server = try await withCheckedThrowingContinuation { continuation in
let bootstrap = ServerBootstrap(group: group)
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelInitializer { channel in
channel.pipeline.configureHTTPServerPipeline().flatMap { _ in
let handler = MockHTTPHandler()
return channel.pipeline.addHandler(handler)
}
}
bootstrap.bind(host: "localhost", port: 0).whenComplete { result in
switch result {
case .success(let channel):
let server = MockHTTPServer(group: group, channel: channel)
channel.pipeline.handler(type: MockHTTPHandler.self).whenSuccess { handler in
handler.server = server
}
continuation.resume(returning: server)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
return server
}
func stop() async throws {
try await channel.close()
}
func addResponse(for path: String, statusCode: HTTPResponseStatus, body: String) {
responses[path] = (statusCode, body)
}
func getResponse(for path: String) -> (HTTPResponseStatus, String)? {
return responses[path]
}
private final class MockHTTPHandler: ChannelInboundHandler {
typealias InboundIn = HTTPServerRequestPart
typealias OutboundOut = HTTPServerResponsePart
weak var server: MockHTTPServer?
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let reqPart = unwrapInboundIn(data)
guard case .head(let head) = reqPart else { return }
guard let server = self.server else {
respond(context: context, status: .internalServerError, body: "Server not found")
return
}
if let (status, body) = server.getResponse(for: head.uri) {
respond(context: context, status: status, body: body)
} else {
respond(context: context, status: .notFound, body: "Not Found")
}
}
private func respond(context: ChannelHandlerContext, status: HTTPResponseStatus, body: String) {
var headers = HTTPHeaders()
headers.add(name: "Content-Type", value: "application/json")
headers.add(name: "Content-Length", value: "\(body.utf8.count)")
let head = HTTPResponseHead(version: .http1_1, status: status, headers: headers)
context.write(wrapOutboundOut(.head(head)), promise: nil)
var buffer = context.channel.allocator.buffer(capacity: body.utf8.count)
buffer.writeString(body)
context.write(wrapOutboundOut(.body(.byteBuffer(buffer))), promise: nil)
context.writeAndFlush(wrapOutboundOut(.end(nil)), promise: nil)
}
}
}
您可以在这个优秀的博客中阅读有关此错误的更多信息。在您的示例中,编译器会引发有关属性
weak var server: MockHTTPServer?
的错误,因为它是可变的,因为 var
关键字可以引入 data races
。