Skip to content

Commit

Permalink
Add support from synchronous options (#80)
Browse files Browse the repository at this point in the history
Motivation:

NIO 2.27.0 added a customisation point to `Channel` to allow options to
be fetched synchronously if the caller is on the appropriate event loop.

Modifications:

- Add synchronous options to SSHChildChannel
- Promote event loop assertions to preconditions

Result:

Callers can fetch channel options synchronously on 'SSHChildChannel's.
  • Loading branch information
glbrntt committed Mar 16, 2021
1 parent 3322426 commit 5d1cfde
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ let package = Package(
.library(name: "NIOSSH", targets: ["NIOSSH"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "2.21.0"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.27.0"),
.package(url: "https://github.com/apple/swift-crypto.git", from: "1.0.0"),
],
targets: [
Expand Down
33 changes: 31 additions & 2 deletions Sources/NIOSSH/Child Channels/SSHChildChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ extension SSHChildChannel: Channel, ChannelCore {
}

private func setOption0<Option: ChannelOption>(_ option: Option, value: Option.Value) throws {
assert(self.eventLoop.inEventLoop)
self.eventLoop.preconditionInEventLoop()

switch option {
case _ as ChannelOptions.Types.AutoReadOption:
Expand All @@ -211,7 +211,7 @@ extension SSHChildChannel: Channel, ChannelCore {
}

private func getOption0<Option: ChannelOption>(_ option: Option) throws -> Option.Value {
assert(self.eventLoop.inEventLoop)
self.eventLoop.preconditionInEventLoop()

switch option {
case _ as SSHChildChannelOptions.Types.LocalChannelIdentifierOption:
Expand Down Expand Up @@ -1133,6 +1133,35 @@ extension SSHChildChannel {
}
}

extension SSHChildChannel {
internal struct SynchronousOptions: NIOSynchronousChannelOptions {
private let channel: SSHChildChannel

fileprivate init(channel: SSHChildChannel) {
self.channel = channel
}

/// Set `option` to `value` on this `Channel`.
///
/// - Important: Must be called on the `EventLoop` the `Channel` is running on.
internal func setOption<Option: ChannelOption>(_ option: Option, value: Option.Value) throws {
try self.channel.setOption0(option, value: value)
}

/// Get the value of `option` for this `Channel`.
///
/// - Important: Must be called on the `EventLoop` the `Channel` is running on.
internal func getOption<Option: ChannelOption>(_ option: Option) throws -> Option.Value {
try self.channel.getOption0(option)
}
}

/// Returns a view of the `Channel` exposing synchronous versions of `setOption` and `getOption`.
public var syncOptions: NIOSynchronousChannelOptions? {
SynchronousOptions(channel: self)
}
}

private extension IOData {
mutating func slicePrefix(_ length: Int) -> IOData {
assert(length < self.readableBytes)
Expand Down
36 changes: 36 additions & 0 deletions Tests/NIOSSHTests/ChildChannelMultiplexerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1657,4 +1657,40 @@ final class ChildChannelMultiplexerTests: XCTestCase {
XCTAssertEqual(readRecorder.reads.count, 5)
XCTAssertTrue(eofHandler.seenEOF)
}

func testChildChannelSupportsSyncOptions() throws {
var createdChannels = 0
let harness = self.harness { channel, _ in
createdChannels += 1

guard let sync = channel.syncOptions else {
XCTFail("\(channel) does not support syncOptions but should")
return channel.eventLoop.makeSucceededFuture(())
}

do {
let autoRead = try sync.getOption(ChannelOptions.autoRead)
try sync.setOption(ChannelOptions.autoRead, value: !autoRead)
XCTAssertNotEqual(try sync.getOption(ChannelOptions.autoRead), autoRead)
} catch {
XCTFail("Unable to get/set autoRead using synchronous options")
}

return channel.eventLoop.makeSucceededFuture(())
}
defer {
harness.finish()
}

XCTAssertEqual(createdChannels, 0)
XCTAssertEqual(harness.flushedMessages.count, 0)

let openRequest = self.openRequest(channelID: 1)
XCTAssertNoThrow(try harness.multiplexer.receiveMessage(openRequest))
XCTAssertEqual(createdChannels, 1)
XCTAssertEqual(harness.flushedMessages.count, 1)
XCTAssertNil(harness.delegate.writes.first?.1)

self.assertChannelOpenConfirmation(harness.flushedMessages.first, recipientChannel: 1)
}
}

0 comments on commit 5d1cfde

Please sign in to comment.