Skip to content

Commit a301bec

Browse files
committed
Dissallow access and removal of Head and Tail ChannelHandlers
Motivation: The Head and Tail ChannelHandlers should not be accessible as these are implementation details of the ChannelPipeline and removal of these will even make the whole ChannelPipeline not work anymore. Modifications: - Make it impossible to access / removal the Head and Tail ChannelHandlers - Exclude the Head and Tail ChannelHandler from the debug String - Add unit tests. Result: More robust ChannelPipeline.
1 parent 61f03a7 commit a301bec

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

Sources/NIO/ChannelPipeline.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,16 @@ public final class ChannelPipeline: ChannelInvoker {
446446
return promise.futureResult
447447
}
448448

449+
/// Returns a `ChannelHandlerContext` which matches.
450+
///
451+
/// This skips head and tail (as these are internal and should not be accessible by the user.
452+
///
453+
/// - parameters:
454+
/// - body: The predicate to execute per `ChannelHandlerContext` in the `ChannelPipeline`.
455+
/// -returns: The `ChannelHandlerContext` that matches or `nil` if non did.
449456
private func contextForPredicate0(_ body: @escaping((ChannelHandlerContext) -> Bool)) -> ChannelHandlerContext? {
450-
var curCtx: ChannelHandlerContext? = self.head
451-
while let ctx = curCtx {
457+
var curCtx: ChannelHandlerContext? = self.head?.next
458+
while let ctx = curCtx, ctx !== self.tail {
452459
if body(ctx) {
453460
return ctx
454461
}
@@ -1422,8 +1429,8 @@ public final class ChannelHandlerContext: ChannelInvoker {
14221429
extension ChannelPipeline: CustomDebugStringConvertible {
14231430
public var debugDescription: String {
14241431
var desc = "ChannelPipeline (\(ObjectIdentifier(self))):\n"
1425-
var node = self.head
1426-
while let ctx = node {
1432+
var node = self.head?.next
1433+
while let ctx = node, ctx !== self.tail {
14271434
let inboundStr = ctx.handler is _ChannelInboundHandler ? "I" : ""
14281435
let outboundStr = ctx.handler is _ChannelOutboundHandler ? "O" : ""
14291436
desc += " \(ctx.name) (\(type(of: ctx.handler))) [\(inboundStr)\(outboundStr)]\n"

Tests/NIOTests/ChannelPipelineTest.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,4 +620,48 @@ class ChannelPipelineTest: XCTestCase {
620620
XCTAssertTrue(try h1 === channel.pipeline.context(handlerType: TestHandler.self).wait().handler)
621621
XCTAssertFalse(try h2 === channel.pipeline.context(handlerType: TestHandler.self).wait().handler)
622622
}
623+
624+
func testContextForHeadOrTail() throws {
625+
let channel = EmbeddedChannel()
626+
627+
defer {
628+
XCTAssertFalse(try channel.finish())
629+
}
630+
631+
do {
632+
_ = try channel.pipeline.context(name: "head").wait()
633+
XCTFail()
634+
} catch let err as ChannelPipelineError where err == .notFound {
635+
/// expected
636+
}
637+
638+
do {
639+
_ = try channel.pipeline.context(name: "tail").wait()
640+
XCTFail()
641+
} catch let err as ChannelPipelineError where err == .notFound {
642+
/// expected
643+
}
644+
}
645+
646+
func testRemoveHeadOrTail() throws {
647+
let channel = EmbeddedChannel()
648+
649+
defer {
650+
XCTAssertFalse(try channel.finish())
651+
}
652+
653+
do {
654+
_ = try channel.pipeline.remove(name: "head").wait()
655+
XCTFail()
656+
} catch let err as ChannelPipelineError where err == .notFound {
657+
/// expected
658+
}
659+
660+
do {
661+
_ = try channel.pipeline.remove(name: "tail").wait()
662+
XCTFail()
663+
} catch let err as ChannelPipelineError where err == .notFound {
664+
/// expected
665+
}
666+
}
623667
}

0 commit comments

Comments
 (0)