# Copyright (c) 2020-2021 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. {.used.} import std/[algorithm, random, sequtils, options], chronos, bearssl, chronicles, testutils/unittests, ./test_utils, ../../eth/utp/utp_router, ../../eth/utp/utp_socket, ../../eth/utp/packets, ../../eth/keys procSuite "Utp socket unit test": let rng = newRng() let testAddress = initTAddress("127.0.0.1", 9079) let testBufferSize = 1024'u32 let defaultRcvOutgoingId = 314'u16 proc packetsToBytes(packets: seq[Packet]): seq[byte] = var resultBytes = newSeq[byte]() for p in packets: resultBytes.add(p.payload) return resultBytes asyncTest "Starting outgoing socket should send Syn packet": let q = newAsyncQueue[Packet]() let defaultConfig = SocketConfig.init() let sock1 = newOutgoingSocket[TransportAddress]( testAddress, initTestSnd(q), defaultConfig, defaultRcvOutgoingId, rng[] ) let fut1 = sock1.startOutgoingSocket() let initialPacket = await q.get() check: initialPacket.header.pType == ST_SYN initialPacket.header.wndSize == defaultConfig.optRcvBuffer await sock1.destroyWait() fut1.cancel() asyncTest "Outgoing socket should re-send syn packet 2 times before declaring failure": let q = newAsyncQueue[Packet]() let sock1 = newOutgoingSocket[TransportAddress]( testAddress, initTestSnd(q), SocketConfig.init(milliseconds(100)), defaultRcvOutgoingId, rng[] ) let fut1 = sock1.startOutgoingSocket() let initialPacket = await q.get() check: initialPacket.header.pType == ST_SYN let resentSynPacket = await q.get() check: resentSynPacket.header.pType == ST_SYN let resentSynPacket1 = await q.get() check: resentSynPacket1.header.pType == ST_SYN # next timeout will should disconnect socket await waitUntil(proc (): bool = sock1.isConnected() == false) check: not sock1.isConnected() await sock1.destroyWait() fut1.cancel() asyncTest "Processing in order ack should make socket connected": let q = newAsyncQueue[Packet]() let initialRemoteSeq = 10'u16 let (sock1, packet) = connectOutGoingSocket(initialRemoteSeq, q) check: sock1.isConnected() await sock1.destroyWait() asyncTest "Processing in order data packet should upload it to buffer and ack packet": let q = newAsyncQueue[Packet]() let initalRemoteSeqNr = 10'u16 let data = @[1'u8, 2'u8, 3'u8] let (outgoingSocket, initialPacket) = connectOutGoingSocket(initalRemoteSeqNr, q) let dataP1 = dataPacket( initalRemoteSeqNr, initialPacket.header.connectionId, initialPacket.header.seqNr, testBufferSize, data, 0 ) await outgoingSocket.processPacket(dataP1) let ack1 = await q.get() check: ack1.header.pType == ST_STATE ack1.header.ackNr == initalRemoteSeqNr let receivedBytes = await outgoingSocket.read(len(data)) check: receivedBytes == data await outgoingSocket.destroyWait() asyncTest "Processing duplicated fresh data packet should ack it and stop processing": let q = newAsyncQueue[Packet]() let initalRemoteSeqNr = 10'u16 let data = @[1'u8, 2'u8, 3'u8] let (outgoingSocket, initialPacket) = connectOutGoingSocket(initalRemoteSeqNr, q) let dataP1 = dataPacket( initalRemoteSeqNr, initialPacket.header.connectionId, initialPacket.header.seqNr, testBufferSize, data, 0 ) await outgoingSocket.processPacket(dataP1) let ack1 = await q.get() check: ack1.header.pType == ST_STATE ack1.header.ackNr == initalRemoteSeqNr let receivedBytes = await outgoingSocket.read(len(data)) check: receivedBytes == data # remote re-send data packet, most probably due to lost ack await outgoingSocket.processPacket(dataP1) let ack2 = await q.get() check: ack2.header.pType == ST_STATE ack2.header.ackNr == initalRemoteSeqNr # we do not upload data one more time outgoingSocket.numOfBytesInIncomingBuffer() == 0'u32 await outgoingSocket.destroyWait() asyncTest "Processing out of order data packet should buffer it until receiving in order one": # TODO test is valid until implementing selective acks let q = newAsyncQueue[Packet]() let initalRemoteSeqNr = 10'u16 let numOfPackets = 10'u16 let (outgoingSocket, initialPacket) = connectOutGoingSocket(initalRemoteSeqNr, q) var packets = generateDataPackets(numOfPackets, initalRemoteSeqNr, initialPacket.header.connectionId, initialPacket.header.seqNr, rng[]) let data = packetsToBytes(packets) # start feeding packets from the last one packets.reverse() for p in packets: await outgoingSocket.processPacket(p) var sentAcks: seq[Packet] = @[] for i in 0'u16.. 0 await outgoingSocket.destroyWait() asyncTest "Re-sent packet should have updated timestamps and ack numbers": let q = newAsyncQueue[Packet]() let initalRemoteSeqNr = 10'u16 let (outgoingSocket, initialPacket) = connectOutGoingSocket(initalRemoteSeqNr, q) let writeResult = await outgoingSocket.write(@[1'u8]) check: writeResult.isOk() let firstSend = await q.get() let secondSend = await q.get() check: # there was sometime between resend but no packet from remote # so timestamp should be updated but not ackNr secondSend.header.timestamp > firstSend.header.timestamp firstSend.header.ackNr == secondSend.header.ackNr let dataP1 = dataPacket( initalRemoteSeqNr, initialPacket.header.connectionId, initialPacket.header.seqNr, testBufferSize, @[1'u8], 0 ) await outgoingSocket.processPacket(dataP1) let ack = await q.get() check: ack.header.pType == ST_STATE let thirdSend = await q.get() check: # as there was some incoming data between resend, both timestamp and ackNr # should be updated thirdSend.header.timestamp > secondSend.header.timestamp thirdSend.header.ackNr > secondSend.header.ackNr await outgoingSocket.destroyWait()