1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import XCTest
@testable import NIO
import NIOTLS
private class ReadCompletedHandler: ChannelInboundHandler {
public typealias InboundIn = Any
public var readCompleteCount: Int
init() {
readCompleteCount = 0
}
public func channelReadComplete(context: ChannelHandlerContext) {
readCompleteCount += 1
}
}
class ApplicationProtocolNegotiationHandlerTests: XCTestCase {
private enum EventType {
case basic
}
private let negotiatedEvent: TLSUserEvent = .handshakeCompleted(negotiatedProtocol: "h2")
private let negotiatedResult: ALPNResult = .negotiated("h2")
func testChannelProvidedToCallback() throws {
let emChannel = EmbeddedChannel()
let loop = emChannel.eventLoop as! EmbeddedEventLoop
var called = false
let handler = ApplicationProtocolNegotiationHandler { result, channel in
called = true
XCTAssertEqual(result, self.negotiatedResult)
XCTAssertTrue(emChannel === channel)
return loop.makeSucceededFuture(())
}
try emChannel.pipeline.addHandler(handler).wait()
emChannel.pipeline.fireUserInboundEventTriggered(negotiatedEvent)
XCTAssertTrue(called)
}
func testIgnoresUnknownUserEvents() throws {
let channel = EmbeddedChannel()
let loop = channel.eventLoop as! EmbeddedEventLoop
let handler = ApplicationProtocolNegotiationHandler { result in
XCTFail("Negotiation fired")
return loop.makeSucceededFuture(())
}
try channel.pipeline.addHandler(handler).wait()
// Fire a pair of events that should be ignored.
channel.pipeline.fireUserInboundEventTriggered(EventType.basic)
channel.pipeline.fireUserInboundEventTriggered(TLSUserEvent.shutdownCompleted)
// The channel handler should still be in the pipeline.
try channel.pipeline.assertContains(handler: handler)
XCTAssertTrue(try channel.finish().isClean)
}
private func negotiateTest(event: TLSUserEvent, expectedResult: ALPNResult) throws {
let channel = EmbeddedChannel()
let loop = channel.eventLoop as! EmbeddedEventLoop
let continuePromise = loop.makePromise(of: Void.self)
var called = false
let handler = ApplicationProtocolNegotiationHandler { result in
XCTAssertEqual(self.negotiatedResult, result)
called = true
return continuePromise.futureResult
}
try channel.pipeline.addHandler(handler).wait()
// Fire the handshake complete event.
channel.pipeline.fireUserInboundEventTriggered(negotiatedEvent)
// At this time the callback should have fired, but the handler should still be in
// the pipeline.
XCTAssertTrue(called)
try channel.pipeline.assertContains(handler: handler)
// Now we fire the future.
continuePromise.succeed(())
// Now the handler should have removed itself from the pipeline.
try channel.pipeline.assertDoesNotContain(handler: handler)
XCTAssertTrue(try channel.finish().isClean)
}
func testCallbackReflectsNotificationResult() throws {
try negotiateTest(event: negotiatedEvent, expectedResult: negotiatedResult)
}
func testCallbackNotesFallbackForNoNegotiation() throws {
try negotiateTest(event: .handshakeCompleted(negotiatedProtocol: ""), expectedResult: .fallback)
}
func testNoBufferingBeforeEventFires() throws {
let channel = EmbeddedChannel()
let loop = channel.eventLoop as! EmbeddedEventLoop
let handler = ApplicationProtocolNegotiationHandler { result in
XCTFail("Should not be called")
return loop.makeSucceededFuture(())
}
try channel.pipeline.addHandler(handler).wait()
// The data we write should not be buffered.
try channel.writeInbound("hello")
XCTAssertNoThrow(XCTAssertEqual(try channel.readInbound()!, "hello"))
XCTAssertTrue(try channel.finish().isClean)
}
func testBufferingWhileWaitingForFuture() throws {
let channel = EmbeddedChannel()
let loop = channel.eventLoop as! EmbeddedEventLoop
let continuePromise = loop.makePromise(of: Void.self)
let handler = ApplicationProtocolNegotiationHandler { result in
continuePromise.futureResult
}
try channel.pipeline.addHandler(handler).wait()
// Fire in the event.
channel.pipeline.fireUserInboundEventTriggered(negotiatedEvent)
// At this point all writes should be buffered.
try channel.writeInbound("writes")
try channel.writeInbound("are")
try channel.writeInbound("buffered")
XCTAssertNoThrow(XCTAssertNil(try channel.readInbound()))
// Complete the pipeline swap.
continuePromise.succeed(())
// Now everything should have been unbuffered.
XCTAssertNoThrow(XCTAssertEqual(try channel.readInbound()!, "writes"))
XCTAssertNoThrow(XCTAssertEqual(try channel.readInbound()!, "are"))
XCTAssertNoThrow(XCTAssertEqual(try channel.readInbound()!, "buffered"))
XCTAssertTrue(try channel.finish().isClean)
}
func testNothingBufferedDoesNotFireReadCompleted() throws {
let channel = EmbeddedChannel()
let loop = channel.eventLoop as! EmbeddedEventLoop
let continuePromise = loop.makePromise(of: Void.self)
let handler = ApplicationProtocolNegotiationHandler { result in
continuePromise.futureResult
}
let readCompleteHandler = ReadCompletedHandler()
try channel.pipeline.addHandler(handler).wait()
try channel.pipeline.addHandler(readCompleteHandler).wait()
// Fire in the event.
channel.pipeline.fireUserInboundEventTriggered(negotiatedEvent)
// At this time, readComplete hasn't fired.
XCTAssertEqual(readCompleteHandler.readCompleteCount, 0)
// Now satisfy the future, which forces data unbuffering. As we haven't buffered any data,
// readComplete should not be fired.
continuePromise.succeed(())
XCTAssertEqual(readCompleteHandler.readCompleteCount, 0)
XCTAssertTrue(try channel.finish().isClean)
}
func testUnbufferingFiresReadCompleted() throws {
let channel = EmbeddedChannel()
let loop = channel.eventLoop as! EmbeddedEventLoop
let continuePromise = loop.makePromise(of: Void.self)
let handler = ApplicationProtocolNegotiationHandler { result in
continuePromise.futureResult
}
let readCompleteHandler = ReadCompletedHandler()
try channel.pipeline.addHandler(handler).wait()
try channel.pipeline.addHandler(readCompleteHandler).wait()
// Fire in the event.
channel.pipeline.fireUserInboundEventTriggered(negotiatedEvent)
// Send a write, which is buffered.
try channel.writeInbound("a write")
// At this time, readComplete hasn't fired.
XCTAssertEqual(readCompleteHandler.readCompleteCount, 1)
// Now satisfy the future, which forces data unbuffering. This should fire readComplete.
continuePromise.succeed(())
XCTAssertNoThrow(XCTAssertEqual(try channel.readInbound()!, "a write"))
XCTAssertEqual(readCompleteHandler.readCompleteCount, 2)
XCTAssertTrue(try channel.finish().isClean)
}
}
|