mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-06-01 18:53:35 +08:00
FixedLengthFrameDecoder
Provide a decoder for frames with a fixed length. Motivation: This was motivated by the issue https://github.com/apple/swift-nio/issues/474 as a good first issue. Modifications: Implemented a FixedLengthFrameDecoder as well as tests. Result: The event loops are now accessible from the outside.
This commit is contained in:
parent
f5512693e3
commit
5efe6977e4
61
Sources/NIOExtras/FixedLengthDecoder.swift
Normal file
61
Sources/NIOExtras/FixedLengthDecoder.swift
Normal file
@ -0,0 +1,61 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 NIO
|
||||
///
|
||||
/// A decoder that splits the received `ByteBuffer` by a fixed number
|
||||
/// of bytes. For example, if you received the following four fragmented packets:
|
||||
///
|
||||
/// +---+----+------+----+
|
||||
/// | A | BC | DEFG | HI |
|
||||
/// +---+----+------+----+
|
||||
///
|
||||
/// A `FixedLengthFrameDecoder` will decode them into the
|
||||
/// following three packets with the fixed length:
|
||||
///
|
||||
/// +-----+-----+-----+
|
||||
/// | ABC | DEF | GHI |
|
||||
/// +-----+-----+-----+
|
||||
///
|
||||
public final class FixedLengthFrameDecoder: ByteToMessageDecoder {
|
||||
public typealias InboundIn = ByteBuffer
|
||||
public typealias InboundOut = ByteBuffer
|
||||
|
||||
public var cumulationBuffer: ByteBuffer?
|
||||
|
||||
private let frameLength: Int
|
||||
|
||||
/// Create `FixedLengthFrameDecoder` with a given frame length.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - frameLength: The length of a frame.
|
||||
public init(frameLength: Int) {
|
||||
self.frameLength = frameLength
|
||||
}
|
||||
|
||||
public func decode(ctx: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState {
|
||||
guard let slice = buffer.readSlice(length: frameLength) else {
|
||||
return .needMoreData
|
||||
}
|
||||
|
||||
ctx.fireChannelRead(self.wrapInboundOut(slice))
|
||||
return .continue
|
||||
}
|
||||
|
||||
public func handlerRemoved(ctx: ChannelHandlerContext) {
|
||||
if let buffer = cumulationBuffer, buffer.readableBytes > 0 {
|
||||
ctx.fireErrorCaught(NIOExtrasErrors.LeftOverBytesError(leftOverBytes: buffer))
|
||||
}
|
||||
}
|
||||
}
|
25
Sources/NIOExtras/NIOExtrasError.swift
Normal file
25
Sources/NIOExtras/NIOExtrasError.swift
Normal file
@ -0,0 +1,25 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 NIO
|
||||
|
||||
public protocol NIOExtrasError: Equatable, Error { }
|
||||
|
||||
/// Errors that are raised in NIOExtras.
|
||||
public enum NIOExtrasErrors {
|
||||
|
||||
/// Error indicating that after an operation some unused bytes are left.
|
||||
public struct LeftOverBytesError: NIOExtrasError {
|
||||
public let leftOverBytes: ByteBuffer
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ import XCTest
|
||||
@testable import NIOExtrasTests
|
||||
|
||||
XCTMain([
|
||||
testCase(FixedLengthFrameDecoderTest.allTests),
|
||||
testCase(QuiescingHelperTest.allTests),
|
||||
])
|
||||
#endif
|
||||
|
36
Tests/NIOExtrasTests/FixedLengthDecoderTest+XCTest.swift
Normal file
36
Tests/NIOExtrasTests/FixedLengthDecoderTest+XCTest.swift
Normal file
@ -0,0 +1,36 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// FixedLengthDecoderTest+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 FixedLengthFrameDecoderTest {
|
||||
|
||||
static var allTests : [(String, (FixedLengthFrameDecoderTest) -> () throws -> Void)] {
|
||||
return [
|
||||
("testDecodeIfFewerBytesAreSent", testDecodeIfFewerBytesAreSent),
|
||||
("testDecodeIfMoreBytesAreSent", testDecodeIfMoreBytesAreSent),
|
||||
("testRemoveHandlerWhenBufferIsNotEmpty", testRemoveHandlerWhenBufferIsNotEmpty),
|
||||
("testRemoveHandlerWhenBufferIsEmpty", testRemoveHandlerWhenBufferIsEmpty),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
104
Tests/NIOExtrasTests/FixedLengthDecoderTest.swift
Normal file
104
Tests/NIOExtrasTests/FixedLengthDecoderTest.swift
Normal file
@ -0,0 +1,104 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
import NIO
|
||||
import NIOExtras
|
||||
|
||||
class FixedLengthFrameDecoderTest: XCTestCase {
|
||||
|
||||
public func testDecodeIfFewerBytesAreSent() throws {
|
||||
let channel = EmbeddedChannel()
|
||||
|
||||
let frameLength = 8
|
||||
try channel.pipeline.add(handler: FixedLengthFrameDecoder(frameLength: frameLength)).wait()
|
||||
|
||||
var buffer = channel.allocator.buffer(capacity: frameLength)
|
||||
buffer.write(string: "xxxx")
|
||||
XCTAssertFalse(try channel.writeInbound(buffer))
|
||||
XCTAssertTrue(try channel.writeInbound(buffer))
|
||||
|
||||
var outputBuffer: ByteBuffer? = channel.readInbound()
|
||||
XCTAssertEqual("xxxxxxxx", outputBuffer?.readString(length: frameLength))
|
||||
XCTAssertFalse(try channel.finish())
|
||||
}
|
||||
|
||||
public func testDecodeIfMoreBytesAreSent() throws {
|
||||
let channel = EmbeddedChannel()
|
||||
|
||||
let frameLength = 8
|
||||
try channel.pipeline.add(handler: FixedLengthFrameDecoder(frameLength: frameLength)).wait()
|
||||
|
||||
var buffer = channel.allocator.buffer(capacity: 19)
|
||||
buffer.write(string: "xxxxxxxxaaaaaaaabbb")
|
||||
XCTAssertTrue(try channel.writeInbound(buffer))
|
||||
|
||||
var outputBuffer: ByteBuffer? = channel.readInbound()
|
||||
XCTAssertEqual("xxxxxxxx", outputBuffer?.readString(length: frameLength))
|
||||
|
||||
outputBuffer = channel.readInbound()
|
||||
XCTAssertEqual("aaaaaaaa", outputBuffer?.readString(length: frameLength))
|
||||
|
||||
outputBuffer = channel.readInbound()
|
||||
XCTAssertNil(outputBuffer?.readString(length: frameLength))
|
||||
XCTAssertFalse(try channel.finish())
|
||||
}
|
||||
|
||||
public func testRemoveHandlerWhenBufferIsNotEmpty() throws {
|
||||
let channel = EmbeddedChannel()
|
||||
|
||||
let frameLength = 8
|
||||
let handler = FixedLengthFrameDecoder(frameLength: frameLength)
|
||||
try channel.pipeline.add(handler: handler).wait()
|
||||
|
||||
var buffer = channel.allocator.buffer(capacity: 15)
|
||||
buffer.write(string: "xxxxxxxxxxxxxxx")
|
||||
XCTAssertTrue(try channel.writeInbound(buffer))
|
||||
|
||||
var outputBuffer: ByteBuffer? = channel.readInbound()
|
||||
XCTAssertEqual("xxxxxxxx", outputBuffer?.readString(length: frameLength))
|
||||
|
||||
_ = try channel.pipeline.remove(handler: handler).wait()
|
||||
XCTAssertThrowsError(try channel.throwIfErrorCaught()) { error in
|
||||
guard let error = error as? NIOExtrasErrors.LeftOverBytesError else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
var expectedBuffer = channel.allocator.buffer(capacity: 7)
|
||||
expectedBuffer.write(string: "xxxxxxx")
|
||||
XCTAssertEqual(error.leftOverBytes, expectedBuffer)
|
||||
}
|
||||
XCTAssertFalse(try channel.finish())
|
||||
}
|
||||
|
||||
public func testRemoveHandlerWhenBufferIsEmpty() throws {
|
||||
let channel = EmbeddedChannel()
|
||||
|
||||
let frameLength = 8
|
||||
let handler = FixedLengthFrameDecoder(frameLength: frameLength)
|
||||
try channel.pipeline.add(handler: handler).wait()
|
||||
|
||||
var buffer = channel.allocator.buffer(capacity: 6)
|
||||
buffer.write(string: "xxxxxxxx")
|
||||
XCTAssertTrue(try channel.writeInbound(buffer))
|
||||
|
||||
var outputBuffer: ByteBuffer? = channel.readInbound()
|
||||
XCTAssertEqual("xxxxxxxx", outputBuffer?.readString(length: frameLength))
|
||||
|
||||
_ = try channel.pipeline.remove(handler: handler).wait()
|
||||
XCTAssertNoThrow(try channel.throwIfErrorCaught())
|
||||
XCTAssertFalse(try channel.finish())
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user