Skip to content

Commit

Permalink
Add removeHandler(context: ChannelHandlerContext) to SynchronousOpe…
Browse files Browse the repository at this point in the history
…rations of ChannelPipeline (#2912)

### Motivation:

As per #2906.

### Modifications:

Added a method to SynchrounousOperations of ChannelPipeline.

### Result:

Now we are able to remove channel handlers by context.

---------

Co-authored-by: Franz Busch <f.busch@apple.com>
  • Loading branch information
supersonicbyte and FranzBusch authored Oct 10, 2024
1 parent 611fa09 commit f6b8f1f
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Sources/NIOCore/ChannelPipeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,17 @@ extension ChannelPipeline {
return promise.futureResult
}

/// Remove a `ChannelHandler` from the `ChannelPipeline`.
///
/// - parameters:
/// - context: the `ChannelHandlerContext` that belongs to `ChannelHandler` that should be removed.
/// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was removed.
public func removeHandler(context: ChannelHandlerContext) -> EventLoopFuture<Void> {
let promise = self.eventLoop.makePromise(of: Void.self)
self._pipeline.removeHandler(context: context, promise: promise)
return promise.futureResult
}

/// Returns the `ChannelHandlerContext` for the given handler instance if it is in
/// the `ChannelPipeline`, if it exists.
///
Expand Down
45 changes: 45 additions & 0 deletions Tests/NIOPosixTests/ChannelPipelineTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,51 @@ class ChannelPipelineTest: XCTestCase {
}
}

func testRemovingByContexSync() throws {
class Handler: ChannelInboundHandler, RemovableChannelHandler {
typealias InboundIn = Never

var removeHandlerCalled = false
var withinRemoveHandler = false

func removeHandler(context: ChannelHandlerContext, removalToken: ChannelHandlerContext.RemovalToken) {
self.removeHandlerCalled = true
self.withinRemoveHandler = true
defer {
self.withinRemoveHandler = false
}
context.leavePipeline(removalToken: removalToken)
}

func handlerRemoved(context: ChannelHandlerContext) {
XCTAssertTrue(self.removeHandlerCalled)
XCTAssertTrue(self.withinRemoveHandler)
}
}

let channel = EmbeddedChannel()
defer {
XCTAssertNoThrow(XCTAssertTrue(try channel.finish().isClean))
}
let allHandlers = [Handler(), Handler(), Handler()]
XCTAssertNoThrow(try channel.pipeline.addHandler(allHandlers[0], name: "the first one to remove").wait())
XCTAssertNoThrow(try channel.pipeline.addHandler(allHandlers[1], name: "the second one to remove").wait())
XCTAssertNoThrow(try channel.pipeline.addHandler(allHandlers[2], name: "the last one to remove").wait())

let firstContext = try! channel.pipeline.syncOperations.context(name: "the first one to remove")
let secondContext = try! channel.pipeline.syncOperations.context(name: "the second one to remove")
let lastContext = try! channel.pipeline.syncOperations.context(name: "the last one to remove")

XCTAssertNoThrow(try channel.pipeline.syncOperations.removeHandler(context: firstContext).wait())
XCTAssertNoThrow(try channel.pipeline.syncOperations.removeHandler(context: secondContext).wait())
XCTAssertNoThrow(try channel.pipeline.syncOperations.removeHandler(context: lastContext).wait())

for handler in allHandlers {
XCTAssertTrue(handler.removeHandlerCalled)
XCTAssertFalse(handler.withinRemoveHandler)
}
}

func testNonRemovableChannelHandlerIsNotRemovable() {
class NonRemovableHandler: ChannelInboundHandler {
typealias InboundIn = Never
Expand Down

0 comments on commit f6b8f1f

Please sign in to comment.