Repository: apple/swift-nio
File: Tests/NIOPosixTests/DatagramChannelTests.swift (Swift)

Code before:

        XCTAssertNoThrow(try sender.setOption(.datagramSegmentSize, value: CInt(segmentSize)).wait())
        XCTAssertNoThrow(try receiver.setOption(.datagramReceiveOffload, value: true).wait())
        let allocator = FixedSizeRecvByteBufferAllocator(capacity: 1 << 16)
        XCTAssertNoThrow(try receiver.setOption(.recvAllocator, value: allocator).wait())

        let buffer = self.firstChannel.allocator.buffer(repeating: 1, count: segmentSize * segments)
        let writeData = AddressedEnvelope(remoteAddress: receiver.localAddress!, data: buffer)
        XCTAssertNoThrow(try sender.writeAndFlush(writeData).wait())

        let received = try receiver.waitForDatagrams(count: 1)
        let hasGoodGROSupport = received.first!.data.readableBytes == buffer.readableBytes

        if !hasGoodGROSupport {
            // Not well supported: check we receive enough datagrams of the expected size.
            let datagrams = try receiver.waitForDatagrams(count: segments)
            for datagram in datagrams {
                XCTAssertEqual(datagram.data.readableBytes, segmentSize)
            }

            #if os(Linux)
            let info = System.systemInfo
            // If our kernel is more recent than 5.15 and we don't have good GRO support then
            // something has gone wrong (or our assumptions about kernel support are incorrect).
            if let major = info.release.major, let minor = info.release.minor {
                if major >= 6 || (major == 5 && minor >= 15) {
                    XCTFail("Platform does not have good GRO support: \(info.release.release)")
                }
            } else {
                XCTFail("Unable to determine Linux x.y release from '\(info.release.release)'")
            }
            #endif
        }

        return hasGoodGROSupport
    }
}

extension System {
    #if os(Linux)
    internal static let systemInfo: SystemInfo = {
        var info = utsname()
        let rc = CNIOLinux_system_info(&info)
        assert(rc == 0)
        return SystemInfo(utsname: info)
    }()

    struct SystemInfo {
        var machine: String
        var nodeName: String
        var sysName: String

Review comment (suggestion):
This is really interesting. So the write succeeds since we send out the data on the socket however when the subsequently get the `ECONNREFUSED` since there was no reachable destination for the packet.

Code after:
        for datagram in datagrams {
            XCTAssertEqual(datagram.data.readableBytes, buffer.readableBytes)
        }
    }

    func testShutdownReadOnConnectedUDP() throws {
        var buffer = self.firstChannel.allocator.buffer(capacity: 256)
        buffer.writeStaticString("hello, world!")

        // Connect and write
        XCTAssertNoThrow(try self.firstChannel.connect(to: self.secondChannel.localAddress!).wait())

        let writeData = AddressedEnvelope(remoteAddress: self.secondChannel.localAddress!, data: buffer)
        XCTAssertNoThrow(try self.firstChannel.writeAndFlush(writeData).wait())
        _ = try self.secondChannel.waitForDatagrams(count: 1)

        // Ok, close on the second channel.
        XCTAssertNoThrow(try self.secondChannel.close(mode: .all).wait())
        print("closed")

        // Write again.
        XCTAssertNoThrow(try self.firstChannel.writeAndFlush(writeData).wait())

        // This should trigger an error.
        let errors = try self.firstChannel.waitForErrors(count: 1)
        XCTAssertEqual((errors[0] as? IOError)?.errnoCode, ECONNREFUSED)
    }

    private func hasGoodGROSupport() throws -> Bool {
        // Source code for UDP_GRO was added in Linux 5.0. However, this support is somewhat limited
        // and some sources indicate support was actually added in 5.10 (perhaps more widely
        // supported). There is no way (or at least, no obvious way) to detect when support was
        // properly fleshed out on a given kernel version.
        //
        // Anecdotally we have observed UDP_GRO works on 5.15 but not on 5.4. The effect of UDP_GRO
        // not working is that datagrams aren't agregated... in other words, GRO not being enabled.
        // This is fine because it's not always the case that datagrams can be aggregated so
        // applications must be able to tolerate this.
        //
        // It does however make testing GRO somewhat challenging. We need to know when we can assert
        // that datagrams will be aggregated. To do this we run a simple check on loopback (as we
        // use this for all other UDP_GRO tests) and check whether datagrams are aggregated on the
        // receive side. If they aren't then we we don't bother with further testing and instead
        // validate that our kernel is older than 5.15.
        try XCTSkipUnless(System.supportsUDPSegmentationOffload, "UDP_SEGMENT (GSO) is not supported on this platform")
        try XCTSkipUnless(System.supportsUDPReceiveOffload, "UDP_GRO is not supported on this platform")
        let sender = try! self.buildChannel(group: self.group)
        let receiver = try! self.buildChannel(group: self.group)
        defer {
            XCTAssertNoThrow(try sender.close().wait())
            XCTAssertNoThrow(try receiver.close().wait())