mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-05-14 17:02:43 +08:00
Motivation Per SwiftNIO's formal version policy, we are ready to drop support for Swift 5.5. Modifications This patch removes the support for 5.5 and all supporting infrastructure. This includes the test generation functionality, which is no longer required, as well as the files generated by that functionality. It updates the dockerfile for 5.8, and it removes all conditional compilation checks that are now definitionally true. Result A nice, clean, 5.6+ codebase
252 lines
10 KiB
Swift
252 lines
10 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2017-2021 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 NIOCore
|
|
|
|
extension ByteBuffer {
|
|
@inlinable
|
|
mutating func get24UInt(
|
|
at index: Int,
|
|
endianness: Endianness = .big
|
|
) -> UInt32? {
|
|
let mostSignificant: UInt16
|
|
let leastSignificant: UInt8
|
|
switch endianness {
|
|
case .big:
|
|
guard let uint16 = self.getInteger(at: index, endianness: .big, as: UInt16.self),
|
|
let uint8 = self.getInteger(at: index + 2, endianness: .big, as: UInt8.self) else { return nil }
|
|
mostSignificant = uint16
|
|
leastSignificant = uint8
|
|
case .little:
|
|
guard let uint8 = self.getInteger(at: index, endianness: .little, as: UInt8.self),
|
|
let uint16 = self.getInteger(at: index + 1, endianness: .little, as: UInt16.self) else { return nil }
|
|
mostSignificant = uint16
|
|
leastSignificant = uint8
|
|
}
|
|
return (UInt32(mostSignificant) << 8) &+ UInt32(leastSignificant)
|
|
}
|
|
@inlinable
|
|
mutating func read24UInt(
|
|
endianness: Endianness = .big
|
|
) -> UInt32? {
|
|
guard let integer = get24UInt(at: self.readerIndex, endianness: endianness) else { return nil }
|
|
self.moveReaderIndex(forwardBy: 3)
|
|
return integer
|
|
}
|
|
}
|
|
|
|
public enum NIOLengthFieldBasedFrameDecoderError: Error {
|
|
/// This error can be thrown by ``LengthFieldBasedFrameDecoder`` if the length field value is larger than `Int.max`
|
|
case lengthFieldValueTooLarge
|
|
/// This error can be thrown by ``LengthFieldBasedFrameDecoder`` if the length field value is larger than `LengthFieldBasedFrameDecoder.maxSupportedLengthFieldSize`
|
|
case lengthFieldValueLargerThanMaxSupportedSize
|
|
}
|
|
|
|
///
|
|
/// A decoder that splits the received `ByteBuffer` by the number of bytes specified in a fixed length header
|
|
/// contained within the buffer.
|
|
///
|
|
/// For example, if you received the following four fragmented packets:
|
|
///
|
|
/// +---+----+------+----+
|
|
/// | A | BC | DEFG | HI |
|
|
/// +---+----+------+----+
|
|
///
|
|
/// Given that the specified header length is 1 byte,
|
|
/// where the first header specifies 3 bytes while the second header specifies 4 bytes,
|
|
/// a ``LengthFieldBasedFrameDecoder`` will decode them into the following packets:
|
|
///
|
|
/// +-----+------+
|
|
/// | BCD | FGHI |
|
|
/// +-----+------+
|
|
///
|
|
/// 'A' and 'E' will be the headers and will not be passed forward.
|
|
///
|
|
public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder {
|
|
/// Maximum supported length field size in bytes of ``LengthFieldBasedFrameDecoder`` and is currently `Int32.max`
|
|
public static let maxSupportedLengthFieldSize: Int = Int(Int32.max)
|
|
|
|
/// An enumeration to describe the length of a piece of data in bytes.
|
|
public enum ByteLength {
|
|
/// One byte
|
|
case one
|
|
/// Two bytes
|
|
case two
|
|
/// Four bytes
|
|
case four
|
|
/// Eight bytes
|
|
case eight
|
|
|
|
fileprivate var bitLength: NIOLengthFieldBitLength {
|
|
switch self {
|
|
case .one: return .oneByte
|
|
case .two: return .twoBytes
|
|
case .four: return .fourBytes
|
|
case .eight: return .eightBytes
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The decoder has two distinct sections of data to read.
|
|
/// Each must be fully present before it is considered as read.
|
|
/// During the time when it is not present the decoder must wait. ``DecoderReadState`` details that waiting state.
|
|
private enum DecoderReadState {
|
|
// Expending a header next.
|
|
case waitingForHeader
|
|
// Expecting the frame next.
|
|
case waitingForFrame(length: Int)
|
|
}
|
|
|
|
/// Incoming data is in `ByteBuffer`
|
|
public typealias InboundIn = ByteBuffer
|
|
/// `ByteBuffer` is type passed to next stage.
|
|
public typealias InboundOut = ByteBuffer
|
|
|
|
@available(*, deprecated, message: "No longer used")
|
|
public var cumulationBuffer: ByteBuffer?
|
|
private var readState: DecoderReadState = .waitingForHeader
|
|
|
|
private let lengthFieldLength: NIOLengthFieldBitLength
|
|
private let lengthFieldEndianness: Endianness
|
|
|
|
/// Create `LengthFieldBasedFrameDecoder` with a given frame length.
|
|
///
|
|
/// - parameters:
|
|
/// - lengthFieldLength: The length of the field specifying the remaining length of the frame.
|
|
/// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame.
|
|
public convenience init(lengthFieldLength: ByteLength, lengthFieldEndianness: Endianness = .big) {
|
|
self.init(lengthFieldBitLength: lengthFieldLength.bitLength, lengthFieldEndianness: lengthFieldEndianness)
|
|
}
|
|
|
|
/// Create `LengthFieldBasedFrameDecoder` with a given frame length.
|
|
///
|
|
/// - parameters:
|
|
/// - lengthFieldBitLength: The length of the field specifying the remaining length of the frame.
|
|
/// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame.
|
|
public init(lengthFieldBitLength: NIOLengthFieldBitLength, lengthFieldEndianness: Endianness = .big) {
|
|
self.lengthFieldLength = lengthFieldBitLength
|
|
self.lengthFieldEndianness = lengthFieldEndianness
|
|
}
|
|
|
|
/// Decode supplied data.
|
|
/// - Parameters:
|
|
/// - context: Calling context.
|
|
/// - buffer: data to decode.
|
|
/// - Returns: `DecodingState` describing what's needed next.
|
|
public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState {
|
|
|
|
if case .waitingForHeader = self.readState {
|
|
try self.readNextLengthFieldToState(buffer: &buffer)
|
|
}
|
|
|
|
guard case .waitingForFrame(let frameLength) = self.readState else {
|
|
return .needMoreData
|
|
}
|
|
|
|
guard let frameBuffer = try self.readNextFrame(buffer: &buffer, frameLength: frameLength) else {
|
|
return .needMoreData
|
|
}
|
|
|
|
context.fireChannelRead(self.wrapInboundOut(frameBuffer))
|
|
|
|
return .continue
|
|
}
|
|
|
|
/// Decode all data supplied. No more is expected after this.
|
|
/// If all data is not exactly consumed reports and error through `context.fireErrorCaught`
|
|
/// - Parameters:
|
|
/// - context: Calling context.
|
|
/// - buffer: The data to decode
|
|
/// - seenEOF: If End of File has been seen.
|
|
/// - Returns: .needMoreData always as all data has been consumed.
|
|
public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState {
|
|
// we'll just try to decode as much as we can as usually
|
|
while case .continue = try self.decode(context: context, buffer: &buffer) {}
|
|
if buffer.readableBytes > 0 {
|
|
context.fireErrorCaught(NIOExtrasErrors.LeftOverBytesError(leftOverBytes: buffer))
|
|
}
|
|
return .needMoreData
|
|
}
|
|
|
|
/// Attempts to read the header data. Updates the status is successful.
|
|
///
|
|
/// - parameters:
|
|
/// - buffer: The buffer containing the integer frame length.
|
|
private func readNextLengthFieldToState(buffer: inout ByteBuffer) throws {
|
|
|
|
// Convert the length field to an integer specifying the length
|
|
guard let lengthFieldValue = try self.readFrameLength(for: &buffer) else {
|
|
return
|
|
}
|
|
|
|
self.readState = .waitingForFrame(length: lengthFieldValue)
|
|
}
|
|
|
|
/// Attempts to read the body data for a given length. Updates the status is successful.
|
|
///
|
|
/// - parameters:
|
|
/// - buffer: The buffer containing the frame data.
|
|
/// - frameLength: The length of the frame data to be read.
|
|
private func readNextFrame(buffer: inout ByteBuffer, frameLength: Int) throws -> ByteBuffer? {
|
|
|
|
guard let contentsFieldSlice = buffer.readSlice(length: frameLength) else {
|
|
return nil
|
|
}
|
|
|
|
self.readState = .waitingForHeader
|
|
|
|
return contentsFieldSlice
|
|
}
|
|
|
|
/// Decodes the specified region of the buffer into an unadjusted frame length. The default implementation is
|
|
/// capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer.
|
|
///
|
|
/// - parameters:
|
|
/// - buffer: The buffer containing the integer frame length.
|
|
private func readFrameLength(for buffer: inout ByteBuffer) throws -> Int? {
|
|
let frameLength: Int?
|
|
switch self.lengthFieldLength.bitLength {
|
|
case .bits8:
|
|
frameLength = buffer.readInteger(endianness: self.lengthFieldEndianness, as: UInt8.self).map { Int($0) }
|
|
case .bits16:
|
|
frameLength = buffer.readInteger(endianness: self.lengthFieldEndianness, as: UInt16.self).map { Int($0) }
|
|
case .bits24:
|
|
frameLength = buffer.read24UInt(endianness: self.lengthFieldEndianness).map { Int($0) }
|
|
case .bits32:
|
|
frameLength = try buffer.readInteger(endianness: self.lengthFieldEndianness, as: UInt32.self).map {
|
|
guard let size = Int(exactly: $0) else {
|
|
throw NIOLengthFieldBasedFrameDecoderError.lengthFieldValueTooLarge
|
|
}
|
|
return size
|
|
}
|
|
case .bits64:
|
|
frameLength = try buffer.readInteger(endianness: self.lengthFieldEndianness, as: UInt64.self).map {
|
|
guard let size = Int(exactly: $0) else {
|
|
throw NIOLengthFieldBasedFrameDecoderError.lengthFieldValueTooLarge
|
|
}
|
|
return size
|
|
}
|
|
}
|
|
|
|
if let frameLength = frameLength,
|
|
frameLength > LengthFieldBasedFrameDecoder.maxSupportedLengthFieldSize {
|
|
throw NIOLengthFieldBasedFrameDecoderError.lengthFieldValueLargerThanMaxSupportedSize
|
|
}
|
|
return frameLength
|
|
}
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
extension LengthFieldBasedFrameDecoder: Sendable {}
|