Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test that we tolerate out-of-band data #1197

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/NIO/Socket.swift
Expand Up @@ -134,7 +134,7 @@ typealias IOVector = iovec
func sendto(pointer: UnsafeRawBufferPointer, destinationPtr: UnsafePointer<sockaddr>, destinationSize: socklen_t) throws -> IOResult<Int> {
return try withUnsafeFileDescriptor { fd in
try Posix.sendto(descriptor: fd, pointer: UnsafeMutableRawPointer(mutating: pointer.baseAddress!),
size: pointer.count, destinationPtr: destinationPtr,
size: pointer.count, flags: 0, destinationPtr: destinationPtr,
destinationSize: destinationSize)
}
}
Expand Down
9 changes: 7 additions & 2 deletions Sources/NIO/System.swift
Expand Up @@ -194,6 +194,7 @@ internal enum Posix {
static let SHUT_RD: CInt = CInt(Darwin.SHUT_RD)
static let SHUT_WR: CInt = CInt(Darwin.SHUT_WR)
static let SHUT_RDWR: CInt = CInt(Darwin.SHUT_RDWR)
static let MSG_OOB: CInt = Darwin.MSG_OOB
#elseif os(Linux) || os(FreeBSD) || os(Android)

#if os(Android)
Expand All @@ -208,6 +209,7 @@ internal enum Posix {
static let SHUT_RD: CInt = CInt(Glibc.SHUT_RD)
static let SHUT_WR: CInt = CInt(Glibc.SHUT_WR)
static let SHUT_RDWR: CInt = CInt(Glibc.SHUT_RDWR)
static let MSG_OOB: CInt = CInt(Glibc.MSG_OOB)
#else
static var SOCK_STREAM: CInt {
fatalError("unsupported OS")
Expand Down Expand Up @@ -365,10 +367,13 @@ internal enum Posix {
}

@inline(never)
public static func sendto(descriptor: CInt, pointer: UnsafeRawPointer, size: size_t,
public static func sendto(descriptor: CInt,
pointer: UnsafeRawPointer,
size: size_t,
flags: CInt,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn’t this an API breaking change? Or am I missing something?

How come we have public methods on an internal enum?

destinationPtr: UnsafePointer<sockaddr>, destinationSize: socklen_t) throws -> IOResult<Int> {
return try wrapSyscallMayBlock {
sysSendTo(descriptor, pointer, size, 0, destinationPtr, destinationSize)
sysSendTo(descriptor, pointer, size, flags, destinationPtr, destinationSize)
}
}

Expand Down
1 change: 1 addition & 0 deletions Tests/NIOTests/ChannelTests+XCTest.swift
Expand Up @@ -82,6 +82,7 @@ extension ChannelTests {
("testTCP_NODELAYisOnByDefault", testTCP_NODELAYisOnByDefault),
("testDescriptionCanBeCalledFromNonEventLoopThreads", testDescriptionCanBeCalledFromNonEventLoopThreads),
("testFixedSizeRecvByteBufferAllocatorSizeIsConstant", testFixedSizeRecvByteBufferAllocatorSizeIsConstant),
("testWeTolerateOutOfBandData", testWeTolerateOutOfBandData),
]
}
}
Expand Down
22 changes: 22 additions & 0 deletions Tests/NIOTests/ChannelTests.swift
Expand Up @@ -2776,7 +2776,29 @@ public final class ChannelTests: XCTestCase {
XCTAssertEqual(1, b1.capacity)
XCTAssertEqual(1, b2.capacity)
XCTAssertEqual(1, allocator.capacity)
}

func testWeTolerateOutOfBandData() {
func runTest() throws {
try withCrossConnectedRawSocketToChannel { socketFD, channel in
let channelRead = channel.eventLoop.makePromise(of: Void.self)
XCTAssertNoThrow(try channel.pipeline.addHandler(FulfillOnFirstEventHandler(channelReadPromise: channelRead)).wait())
var data = [UInt8(ascii: "x")]
try channel.remoteAddress!.withSockAddr { sockAddrPtr, sockAddrSize in
for _ in 0..<100 {
XCTAssertNoThrow(try Posix.sendto(descriptor: socketFD,
pointer: &data,
size: 1,
flags: Posix.MSG_OOB,
destinationPtr: sockAddrPtr,
destinationSize: .init(sockAddrSize)))
}
Thread.sleep(until: .init(timeIntervalSinceNow: 0.01))
XCTAssertFalse(channelRead.futureResult.isFulfilled)
}
}
}
XCTAssertNoThrow(try runTest())
}
}

Expand Down
31 changes: 31 additions & 0 deletions Tests/NIOTests/TestUtils.swift
Expand Up @@ -653,3 +653,34 @@ extension EventLoopFuture {
}
}
}

func withCrossConnectedRawSocketToChannel<R>(file: StaticString = #file,
line: UInt = #line,
_ body: (CInt, Channel) throws -> R) throws -> R? {
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer {
XCTAssertNoThrow(try group.syncShutdownGracefully(), file: file, line: line)
}

// socketpair doesn't work for AF_INET (at least on Darwin) so let's build it ourselves.
let serverSocket = try ServerSocket(protocolFamily: AF_INET, setNonBlocking: false)
defer {
XCTAssertNoThrow(try serverSocket.close(), file: file, line: line)
}
try serverSocket.bind(to: .init(ipAddress: "127.0.0.1", port: 0))
try serverSocket.listen()

let clientChannel = ClientBootstrap(group: group).connect(to: try serverSocket.localAddress())
let acceptedSocket = try serverSocket.accept()! /* only returns nil if in non-blocking mode */
defer {
try? acceptedSocket.close()
}
let channel = try clientChannel.wait()
defer {
XCTAssertNoThrow(try channel.syncCloseAcceptingAlreadyClosed(), file: file, line: line)
}

return try acceptedSocket.withUnsafeFileDescriptor { acceptedFD in
try body(acceptedFD, channel)
}
}