diff --git a/Sources/NIOHTTP2/HTTP2Frame.swift b/Sources/NIOHTTP2/HTTP2Frame.swift index 244dbd7a..080df80d 100644 --- a/Sources/NIOHTTP2/HTTP2Frame.swift +++ b/Sources/NIOHTTP2/HTTP2Frame.swift @@ -252,27 +252,38 @@ public struct HTTP2Frame { } } -extension HTTP2Frame: HTTP2FrameConvertible, HTTP2FramePayloadConvertible { - init(http2Frame: HTTP2Frame) { - self = http2Frame - } +extension HTTP2Frame.FramePayload { + /// A shorthand heuristic for how many bytes we assume a frame consumes on the wire. + /// + /// Here we concern ourselves only with per-stream frames: that is, `HEADERS`, `DATA`, + /// `WINDOW_UDPATE`, `RST_STREAM`, and I guess `PRIORITY`. As a simple heuristic we + /// hard code fixed lengths for fixed length frames, use a calculated length for + /// variable length frames, and just ignore encoded headers because it's not worth doing a better + /// job. + var estimatedFrameSize: Int { + let frameHeaderSize = 9 - func makeHTTP2Frame(streamID: HTTP2StreamID) -> HTTP2Frame { - assert(self.streamID == streamID, "streamID does not match") - return self - } -} - -extension HTTP2Frame.FramePayload: HTTP2FrameConvertible, HTTP2FramePayloadConvertible { - var payload: HTTP2Frame.FramePayload { - return self - } - - init(http2Frame: HTTP2Frame) { - self = http2Frame.payload - } - - func makeHTTP2Frame(streamID: HTTP2StreamID) -> HTTP2Frame { - return HTTP2Frame(streamID: streamID, payload: self) + switch self { + case .data(let d): + let paddingBytes = d.paddingBytes.map { $0 + 1 } ?? 0 + return d.data.readableBytes + paddingBytes + frameHeaderSize + case .headers(let h): + let paddingBytes = h.paddingBytes.map { $0 + 1 } ?? 0 + return paddingBytes + frameHeaderSize + case .priority: + return frameHeaderSize + 5 + case .pushPromise(let p): + // Like headers, this is variably size, and we just ignore the encoded headers because + // it's not worth having a heuristic. + let paddingBytes = p.paddingBytes.map { $0 + 1 } ?? 0 + return paddingBytes + frameHeaderSize + case .rstStream: + return frameHeaderSize + 4 + case .windowUpdate: + return frameHeaderSize + 4 + default: + // Unknown or unexpected control frame: say 9 bytes. + return frameHeaderSize + } } } diff --git a/Sources/NIOHTTP2/HTTP2FrameConvertible.swift b/Sources/NIOHTTP2/HTTP2FrameConvertible.swift deleted file mode 100644 index 181ed9ba..00000000 --- a/Sources/NIOHTTP2/HTTP2FrameConvertible.swift +++ /dev/null @@ -1,64 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the SwiftNIO open source project -// -// Copyright (c) 2020 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 -// -//===----------------------------------------------------------------------===// - -protocol HTTP2FrameConvertible { - /// Initialize `Self` from an `HTTP2Frame`. - init(http2Frame: HTTP2Frame) - - /// Makes an `HTTPFrame` with the given `streamID`. - /// - /// - Parameter streamID: The `streamID` to use when constructing the frame. - func makeHTTP2Frame(streamID: HTTP2StreamID) -> HTTP2Frame -} - -protocol HTTP2FramePayloadConvertible { - /// Makes a `HTTP2Frame.FramePayload`. - var payload: HTTP2Frame.FramePayload { get } -} - -extension HTTP2FrameConvertible where Self: HTTP2FramePayloadConvertible { - /// A shorthand heuristic for how many bytes we assume a frame consumes on the wire. - /// - /// Here we concern ourselves only with per-stream frames: that is, `HEADERS`, `DATA`, - /// `WINDOW_UDPATE`, `RST_STREAM`, and I guess `PRIORITY`. As a simple heuristic we - /// hard code fixed lengths for fixed length frames, use a calculated length for - /// variable length frames, and just ignore encoded headers because it's not worth doing a better - /// job. - var estimatedFrameSize: Int { - let frameHeaderSize = 9 - - switch self.payload { - case .data(let d): - let paddingBytes = d.paddingBytes.map { $0 + 1 } ?? 0 - return d.data.readableBytes + paddingBytes + frameHeaderSize - case .headers(let h): - let paddingBytes = h.paddingBytes.map { $0 + 1 } ?? 0 - return paddingBytes + frameHeaderSize - case .priority: - return frameHeaderSize + 5 - case .pushPromise(let p): - // Like headers, this is variably size, and we just ignore the encoded headers because - // it's not worth having a heuristic. - let paddingBytes = p.paddingBytes.map { $0 + 1 } ?? 0 - return paddingBytes + frameHeaderSize - case .rstStream: - return frameHeaderSize + 4 - case .windowUpdate: - return frameHeaderSize + 4 - default: - // Unknown or unexpected control frame: say 9 bytes. - return frameHeaderSize - } - } -} diff --git a/Sources/NIOHTTP2/HTTP2StreamChannel.swift b/Sources/NIOHTTP2/HTTP2StreamChannel.swift index 4003635f..96b3416a 100644 --- a/Sources/NIOHTTP2/HTTP2StreamChannel.swift +++ b/Sources/NIOHTTP2/HTTP2StreamChannel.swift @@ -121,20 +121,40 @@ private enum StreamChannelState { } } -/// An `HTTP2StreamChannel` which deals in `HTTPFrame`s. -typealias HTTP2FrameBasedStreamChannel = HTTP2StreamChannel +// The type of data read from and written to the channel. +enum HTTP2StreamDataType { + /// `HTTP2Frame` + case frame + /// `HTTP2Frame.FramePayload` + case framePayload +} + +private enum HTTP2StreamData { + case frame(HTTP2Frame) + case framePayload(HTTP2Frame.FramePayload) + + var estimatedFrameSize: Int { + switch self { + case .frame(let frame): + return frame.payload.estimatedFrameSize + case .framePayload(let payload): + return payload.estimatedFrameSize + } + } +} -/// An `HTTP2StreamChannel` which reads and writes `HTTPFrame.FramePayload`s. -typealias HTTP2PayloadBasedStreamChannel = HTTP2StreamChannel +final class HTTP2StreamChannel: Channel, ChannelCore { + /// The stream data type of the channel. + private let streamDataType: HTTP2StreamDataType -final class HTTP2StreamChannel: Channel, ChannelCore { internal init(allocator: ByteBufferAllocator, parent: Channel, multiplexer: HTTP2StreamMultiplexer, streamID: HTTP2StreamID?, targetWindowSize: Int32, outboundBytesHighWatermark: Int, - outboundBytesLowWatermark: Int) { + outboundBytesLowWatermark: Int, + streamDataType: HTTP2StreamDataType) { self.allocator = allocator self.closePromise = parent.eventLoop.makePromise() self.localAddress = parent.localAddress @@ -147,6 +167,7 @@ final class HTTP2StreamChannel EventLoopFuture)?, userPromise promise: EventLoopPromise?){ + assert(self.streamDataType == .frame) // We need to configure this channel. This involves doing four things: // 1. Setting our autoRead state from the parent // 2. Calling the initializer, if provided. @@ -176,6 +198,7 @@ final class HTTP2StreamChannel EventLoopFuture)?, userPromise promise: EventLoopPromise?){ + assert(self.streamDataType == .framePayload) // We need to configure this channel. This involves doing four things: // 1. Setting our autoRead state from the parent // 2. Calling the initializer, if provided. @@ -385,7 +408,7 @@ final class HTTP2StreamChannel = CircularBuffer(initialCapacity: 8) + private var pendingReads: CircularBuffer = CircularBuffer(initialCapacity: 8) /// Whether `autoRead` is enabled. By default, all `HTTP2StreamChannel` objects inherit their `autoRead` /// state from their parent. @@ -399,7 +422,7 @@ final class HTTP2StreamChannel?)> = MarkedCircularBuffer(initialCapacity: 8) + private var pendingWrites: MarkedCircularBuffer<(HTTP2StreamData, EventLoopPromise?)> = MarkedCircularBuffer(initialCapacity: 8) /// A list node used to hold stream channels. internal var streamChannelListNode: StreamChannelListNode = StreamChannelListNode() @@ -425,13 +448,19 @@ final class HTTP2StreamChannel 0 { let frame = self.pendingReads.removeFirst() + let anyStreamData: NIOAny let dataLength: Int? + switch self.streamDataType { + case .frame: + anyStreamData = NIOAny(frame) + case .framePayload: + anyStreamData = NIOAny(frame.payload) + } + switch frame.payload { case .data(let data): dataLength = data.data.readableBytes @@ -639,7 +676,7 @@ private extension HTTP2StreamChannel { dataLength = nil } - self.pipeline.fireChannelRead(NIOAny(frame)) + self.pipeline.fireChannelRead(anyStreamData) if let size = dataLength, let increment = self.windowManager.bufferedFrameEmitted(size: size) { // To have a pending read, we must have a stream ID. @@ -665,9 +702,17 @@ private extension HTTP2StreamChannel { } while self.pendingWrites.hasMark { - let (outbound, promise) = self.pendingWrites.removeFirst() - // This unwrap is okay: we just ensured that `self.streamID` was set above. - let frame = outbound.makeHTTP2Frame(streamID: self.streamID!) + let (streamData, promise) = self.pendingWrites.removeFirst() + let frame: HTTP2Frame + + switch streamData { + case .frame(let f): + frame = f + case .framePayload(let payload): + // This unwrap is okay: we just ensured that `self.streamID` was set above. + frame = HTTP2Frame(streamID: self.streamID!, payload: payload) + } + self.receiveOutboundFrame(frame, promise: promise) } self.multiplexer.childChannelFlush() @@ -694,8 +739,6 @@ internal extension HTTP2StreamChannel { return } - let message = Message(http2Frame: frame) - // Record the size of the frame so that when we receive a window update event our // calculation on whether we emit a WINDOW_UPDATE frame is based on the bytes we have // actually delivered into the pipeline. @@ -710,9 +753,9 @@ internal extension HTTP2StreamChannel { // No further window update frames should be sent. self.windowManager.closed = true } - self.pendingReads.append(message) - } + self.pendingReads.append(frame) + } /// Called when a frame is sent to the network. /// diff --git a/Sources/NIOHTTP2/MultiplexerAbstractChannel.swift b/Sources/NIOHTTP2/MultiplexerAbstractChannel.swift index caa64908..8f922706 100644 --- a/Sources/NIOHTTP2/MultiplexerAbstractChannel.swift +++ b/Sources/NIOHTTP2/MultiplexerAbstractChannel.swift @@ -24,11 +24,7 @@ import NIO /// Note that while this is a `struct`, this `struct` has _reference semantics_. /// The implementation of `Equatable` & `Hashable` on this type reinforces that requirement. struct MultiplexerAbstractChannel { - private var baseChannel: BaseChannel - - private init(baseChannel: BaseChannel) { - self.baseChannel = baseChannel - } + private var baseChannel: HTTP2StreamChannel init(allocator: ByteBufferAllocator, parent: Channel, @@ -41,31 +37,29 @@ struct MultiplexerAbstractChannel { switch inboundStreamStateInitializer { case .includesStreamID: assert(streamID != nil) - self.init(baseChannel: .frameBased(.init(allocator: allocator, - parent: parent, - multiplexer: multiplexer, - streamID: streamID, - targetWindowSize: targetWindowSize, - outboundBytesHighWatermark: outboundBytesHighWatermark, - outboundBytesLowWatermark: outboundBytesLowWatermark))) + self.baseChannel = .init(allocator: allocator, + parent: parent, + multiplexer: multiplexer, + streamID: streamID, + targetWindowSize: targetWindowSize, + outboundBytesHighWatermark: outboundBytesHighWatermark, + outboundBytesLowWatermark: outboundBytesLowWatermark, + streamDataType: .frame) + case .excludesStreamID: - self.init(baseChannel: .payloadBased(.init(allocator: allocator, - parent: parent, - multiplexer: multiplexer, - streamID: streamID, - targetWindowSize: targetWindowSize, - outboundBytesHighWatermark: outboundBytesHighWatermark, - outboundBytesLowWatermark: outboundBytesLowWatermark))) + self.baseChannel = .init(allocator: allocator, + parent: parent, + multiplexer: multiplexer, + streamID: streamID, + targetWindowSize: targetWindowSize, + outboundBytesHighWatermark: outboundBytesHighWatermark, + outboundBytesLowWatermark: outboundBytesLowWatermark, + streamDataType: .framePayload) } } } extension MultiplexerAbstractChannel { - enum BaseChannel { - case frameBased(HTTP2FrameBasedStreamChannel) - case payloadBased(HTTP2PayloadBasedStreamChannel) - } - enum InboundStreamStateInitializer { case includesStreamID(((Channel, HTTP2StreamID) -> EventLoopFuture)?) case excludesStreamID(((Channel) -> EventLoopFuture)?) @@ -75,176 +69,84 @@ extension MultiplexerAbstractChannel { // MARK: API for HTTP2StreamMultiplexer extension MultiplexerAbstractChannel { var streamID: HTTP2StreamID? { - switch self.baseChannel { - case .frameBased(let base): - return base.streamID - case .payloadBased(let base): - return base.streamID - } + return self.baseChannel.streamID } var channelID: ObjectIdentifier { - switch self.baseChannel { - case .frameBased(let base): - return ObjectIdentifier(base) - case .payloadBased(let base): - return ObjectIdentifier(base) - } + return ObjectIdentifier(self.baseChannel) } var inList: Bool { - switch self.baseChannel { - case .frameBased(let base): - return base.inList - case .payloadBased(let base): - return base.inList - } + return self.baseChannel.inList } var streamChannelListNode: StreamChannelListNode { get { - switch self.baseChannel { - case .frameBased(let base): - return base.streamChannelListNode - case .payloadBased(let base): - return base.streamChannelListNode - } + return self.baseChannel.streamChannelListNode } nonmutating set { - switch self.baseChannel { - case .frameBased(let base): - base.streamChannelListNode = newValue - case .payloadBased(let base): - base.streamChannelListNode = newValue - } + self.baseChannel.streamChannelListNode = newValue } } func configureInboundStream(initializer: InboundStreamStateInitializer) { - switch (self.baseChannel, initializer) { - case (.frameBased(let base), .includesStreamID(let initializer)): - base.configure(initializer: initializer, userPromise: nil) - case (.payloadBased(let base), .excludesStreamID(let initializer)): - base.configure(initializer: initializer, userPromise: nil) - case (.frameBased, .excludesStreamID), - (.payloadBased, .includesStreamID): - // We create the base channel based on the type inbound initializer we receive - // in `init`, so this should never happen. - preconditionFailure("Invalid base channel and inbound stream state combination") + switch initializer { + case .includesStreamID(let initializer): + self.baseChannel.configure(initializer: initializer, userPromise: nil) + case .excludesStreamID(let initializer): + self.baseChannel.configure(initializer: initializer, userPromise: nil) } } func configure(initializer: ((Channel, HTTP2StreamID) -> EventLoopFuture)?, userPromise promise: EventLoopPromise?) { - switch self.baseChannel { - case .frameBased(let base): - base.configure(initializer: initializer, userPromise: promise) - case .payloadBased: - fatalError("Can't configure a payload based channel with this initializer.") - } + self.baseChannel.configure(initializer: initializer, userPromise: promise) } func configure(initializer: ((Channel) -> EventLoopFuture)?, userPromise promise: EventLoopPromise?) { - switch self.baseChannel { - case .frameBased: - fatalError("Can't configure a frame based channel with this initializer.") - case .payloadBased(let base): - base.configure(initializer: initializer, userPromise: promise) - } + self.baseChannel.configure(initializer: initializer, userPromise: promise) } func performActivation() { - switch self.baseChannel { - case .frameBased(let base): - base.performActivation() - case .payloadBased(let base): - base.performActivation() - } + self.baseChannel.performActivation() } func networkActivationReceived() { - switch self.baseChannel { - case .frameBased(let base): - base.networkActivationReceived() - case .payloadBased(let base): - base.networkActivationReceived() - } + self.baseChannel.networkActivationReceived() } func receiveInboundFrame(_ frame: HTTP2Frame) { - switch self.baseChannel { - case .frameBased(let base): - base.receiveInboundFrame(frame) - case .payloadBased(let base): - base.receiveInboundFrame(frame) - } + self.baseChannel.receiveInboundFrame(frame) } func receiveParentChannelReadComplete() { - switch self.baseChannel { - case .frameBased(let base): - base.receiveParentChannelReadComplete() - case .payloadBased(let base): - base.receiveParentChannelReadComplete() - } + self.baseChannel.receiveParentChannelReadComplete() } func initialWindowSizeChanged(delta: Int) { - switch self.baseChannel { - case .frameBased(let base): - base.initialWindowSizeChanged(delta: delta) - case .payloadBased(let base): - base.initialWindowSizeChanged(delta: delta) - } + self.baseChannel.initialWindowSizeChanged(delta: delta) } func receiveWindowUpdatedEvent(_ windowSize: Int) { - switch self.baseChannel { - case .frameBased(let base): - base.receiveWindowUpdatedEvent(windowSize) - case .payloadBased(let base): - base.receiveWindowUpdatedEvent(windowSize) - } + self.baseChannel.receiveWindowUpdatedEvent(windowSize) } func parentChannelWritabilityChanged(newValue: Bool) { - switch self.baseChannel { - case .frameBased(let base): - base.parentChannelWritabilityChanged(newValue: newValue) - case .payloadBased(let base): - base.parentChannelWritabilityChanged(newValue: newValue) - } + self.baseChannel.parentChannelWritabilityChanged(newValue: newValue) } func receiveStreamClosed(_ reason: HTTP2ErrorCode?) { - switch self.baseChannel { - case .frameBased(let base): - base.receiveStreamClosed(reason) - case .payloadBased(let base): - base.receiveStreamClosed(reason) - } + self.baseChannel.receiveStreamClosed(reason) } } extension MultiplexerAbstractChannel: Equatable { static func ==(lhs: MultiplexerAbstractChannel, rhs: MultiplexerAbstractChannel) -> Bool { - switch (lhs.baseChannel, rhs.baseChannel) { - case (.frameBased(let lhs), .frameBased(let rhs)): - return lhs === rhs - case (.payloadBased(let lhs), .payloadBased(let rhs)): - return lhs === rhs - case (.frameBased, .payloadBased), (.payloadBased, .frameBased): - return false - } + return lhs.baseChannel === rhs.baseChannel } } extension MultiplexerAbstractChannel: Hashable { func hash(into hasher: inout Hasher) { - switch self.baseChannel { - case .frameBased(let base): - hasher.combine(ObjectIdentifier(base)) - case .payloadBased(let base): - hasher.combine(ObjectIdentifier(base)) - } + hasher.combine(ObjectIdentifier(self.baseChannel)) } } diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index dadcadf0..d1f40eea 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -44,7 +44,6 @@ class LinuxMainRunnerImpl: LinuxMainRunner { testCase(HPACKCodingTests.allTests), testCase(HPACKIntegrationTests.allTests), testCase(HPACKRegressionTests.allTests), - testCase(HTTP2FrameConvertibleTests.allTests), testCase(HTTP2FrameParserTests.allTests), testCase(HTTP2FramePayloadStreamMultiplexerTests.allTests), testCase(HTTP2FramePayloadToHTTP1CodecTests.allTests), diff --git a/Tests/NIOHTTP2Tests/HTTP2FrameConvertibleTests+XCTest.swift b/Tests/NIOHTTP2Tests/HTTP2FrameConvertibleTests+XCTest.swift deleted file mode 100644 index 2d723aa7..00000000 --- a/Tests/NIOHTTP2Tests/HTTP2FrameConvertibleTests+XCTest.swift +++ /dev/null @@ -1,35 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// HTTP2FrameConvertibleTests+XCTest.swift -// -import XCTest - -/// -/// NOTE: This file was generated by generate_linux_tests.rb -/// -/// Do NOT edit this file directly as it will be regenerated automatically when needed. -/// - -extension HTTP2FrameConvertibleTests { - - @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") - static var allTests : [(String, (HTTP2FrameConvertibleTests) -> () throws -> Void)] { - return [ - ("testHTTP2FrameConvertible", testHTTP2FrameConvertible), - ("testHTTP2FramePayloadConvertible", testHTTP2FramePayloadConvertible), - ] - } -} - diff --git a/Tests/NIOHTTP2Tests/HTTP2FrameConvertibleTests.swift b/Tests/NIOHTTP2Tests/HTTP2FrameConvertibleTests.swift deleted file mode 100644 index 3976ec0e..00000000 --- a/Tests/NIOHTTP2Tests/HTTP2FrameConvertibleTests.swift +++ /dev/null @@ -1,60 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the SwiftNIO open source project -// -// Copyright (c) 2020 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 -// -//===----------------------------------------------------------------------===// -@testable import NIOHTTP2 -import XCTest - -final class HTTP2FrameConvertibleTests: XCTestCase { - func testHTTP2FrameConvertible() { - func checkConversion(from convertible: Convertible, line: UInt = #line) { - let frame = convertible.makeHTTP2Frame(streamID: 42) - - XCTAssertEqual(frame.streamID, 42) - switch frame.payload { - case .settings(.ack): - () // Expected - default: - XCTFail("Unexpected frame payload '\(frame.payload)'", line: line) - } - } - - // Boring case: frame from a frame. - let frame = HTTP2Frame(streamID: 42, payload: .settings(.ack)) - checkConversion(from: frame) - - // Marginally less boring: frame from a payload. - let payload = HTTP2Frame.FramePayload.settings(.ack) - checkConversion(from: payload) - } - - func testHTTP2FramePayloadConvertible() { - func checkConversion(from convertible: Convertible, line: UInt = #line) { - let payload = convertible.payload - - switch payload { - case .settings(.ack): - () // Expected - default: - XCTFail("Unexpected frame payload '\(payload)'", line: line) - } - } - - // Boring case: payload from a payload. - let payload = HTTP2Frame.FramePayload.settings(.ack) - checkConversion(from: payload) - - // Marginally less boring: payload from a frame. - let frame = HTTP2Frame(streamID: 42, payload: .settings(.ack)) - checkConversion(from: frame) - } -} diff --git a/Tests/NIOHTTP2Tests/TestUtilities.swift b/Tests/NIOHTTP2Tests/TestUtilities.swift index 4811c00f..ca3d9ac3 100644 --- a/Tests/NIOHTTP2Tests/TestUtilities.swift +++ b/Tests/NIOHTTP2Tests/TestUtilities.swift @@ -492,8 +492,8 @@ extension HTTP2Frame.FramePayload { } func assertGoAwayFramePayload(lastStreamID: HTTP2StreamID, errorCode: UInt32, opaqueData: [UInt8]?, file: StaticString = #file, line: UInt = #line) { - guard case .goAway(let actualLastStreamID, let actualErrorCode, let actualOpaqueData) = self.payload else { - XCTFail("Expected GOAWAY frame, got \(self.payload) instead", file: (file), line: line) + guard case .goAway(let actualLastStreamID, let actualErrorCode, let actualOpaqueData) = self else { + XCTFail("Expected GOAWAY frame, got \(self) instead", file: (file), line: line) return } @@ -516,8 +516,8 @@ extension HTTP2Frame.FramePayload { } func assertPingFramePayload(ack: Bool, opaqueData: HTTP2PingData, file: StaticString = #file, line: UInt = #line) { - guard case .ping(let actualPingData, let actualAck) = self.payload else { - XCTFail("Expected PING frame, got \(self.payload) instead", file: (file), line: line) + guard case .ping(let actualPingData, let actualAck) = self else { + XCTFail("Expected PING frame, got \(self) instead", file: (file), line: line) return } @@ -566,7 +566,7 @@ extension HTTP2Frame.FramePayload { func assertWindowUpdateFramePayload(windowIncrement: Int, file: StaticString = #file, line: UInt = #line) { guard case .windowUpdate(let actualWindowIncrement) = self else { - XCTFail("Expected WINDOW_UPDATE frame, got \(self.payload) instead", file: (file), line: line) + XCTFail("Expected WINDOW_UPDATE frame, got \(self) instead", file: (file), line: line) return } @@ -631,7 +631,7 @@ extension HTTP2Frame.FramePayload { func assertAlternativeServiceFramePayload(origin: String?, field: ByteBuffer?, file: StaticString = #file, line: UInt = #line) { guard case .alternativeService(let actualOrigin, let actualField) = self else { - XCTFail("Expected ALTSVC frame, got \(self.payload) instead", file: (file), line: line) + XCTFail("Expected ALTSVC frame, got \(self) instead", file: (file), line: line) return }