2021-10-11 12:16:06 +00:00
|
|
|
# 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
|
2021-10-19 11:36:57 +00:00
|
|
|
sequtils,
|
|
|
|
chronos, bearssl,
|
2021-10-11 12:16:06 +00:00
|
|
|
testutils/unittests,
|
|
|
|
../../eth/utp/utp_protocol,
|
|
|
|
../../eth/keys
|
|
|
|
|
2021-10-19 11:36:57 +00:00
|
|
|
proc generateByteArray(rng: var BrHmacDrbgContext, length: int): seq[byte] =
|
|
|
|
var bytes = newSeq[byte](length)
|
|
|
|
brHmacDrbgGenerate(rng, bytes)
|
|
|
|
return bytes
|
|
|
|
|
|
|
|
type AssertionCallback = proc(): bool {.gcsafe, raises: [Defect].}
|
|
|
|
|
|
|
|
proc waitUntil(f: AssertionCallback): Future[void] {.async.} =
|
|
|
|
while true:
|
|
|
|
let res = f()
|
|
|
|
if res:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
await sleepAsync(milliseconds(50))
|
|
|
|
|
|
|
|
proc transferData(sender: UtpSocket, receiver: UtpSocket, data: seq[byte]): Future[seq[byte]] {.async.}=
|
|
|
|
let bytesWritten = await sender.write(data)
|
|
|
|
doAssert bytesWritten == len(data)
|
|
|
|
let received = await receiver.read(len(data))
|
|
|
|
return received
|
|
|
|
|
2021-10-11 12:16:06 +00:00
|
|
|
procSuite "Utp protocol tests":
|
|
|
|
let rng = newRng()
|
|
|
|
|
2021-10-19 11:36:57 +00:00
|
|
|
proc setAcceptedCallback(event: AsyncEvent): AcceptConnectionCallback =
|
|
|
|
return (
|
|
|
|
proc(server: UtpProtocol, client: UtpSocket): Future[void] =
|
|
|
|
let fut = newFuture[void]()
|
|
|
|
event.fire()
|
|
|
|
fut.complete()
|
|
|
|
fut
|
|
|
|
)
|
|
|
|
|
|
|
|
proc setIncomingSocketCallback(socketPromise: Future[UtpSocket]): AcceptConnectionCallback =
|
|
|
|
return (
|
|
|
|
proc(server: UtpProtocol, client: UtpSocket): Future[void] =
|
|
|
|
let fut = newFuture[void]()
|
|
|
|
socketPromise.complete(client)
|
|
|
|
fut.complete()
|
|
|
|
fut
|
|
|
|
)
|
|
|
|
|
2021-10-11 12:16:06 +00:00
|
|
|
asyncTest "Success connect to remote host":
|
2021-10-19 11:36:57 +00:00
|
|
|
let server1Called = newAsyncEvent()
|
2021-10-11 12:16:06 +00:00
|
|
|
let address = initTAddress("127.0.0.1", 9079)
|
2021-10-19 11:36:57 +00:00
|
|
|
let utpProt1 = UtpProtocol.new(setAcceptedCallback(server1Called), address)
|
2021-10-11 12:16:06 +00:00
|
|
|
|
2021-10-19 11:36:57 +00:00
|
|
|
var server2Called = newAsyncEvent()
|
2021-10-11 12:16:06 +00:00
|
|
|
let address1 = initTAddress("127.0.0.1", 9080)
|
2021-10-19 11:36:57 +00:00
|
|
|
let utpProt2 = UtpProtocol.new(setAcceptedCallback(server2Called), address1)
|
2021-10-11 12:16:06 +00:00
|
|
|
|
|
|
|
let sock = await utpProt1.connectTo(address1)
|
2021-10-19 11:36:57 +00:00
|
|
|
|
|
|
|
# this future will be completed when we called accepted connection callback
|
|
|
|
await server2Called.wait()
|
|
|
|
|
2021-10-11 12:16:06 +00:00
|
|
|
check:
|
|
|
|
sock.isConnected()
|
2021-10-15 11:38:51 +00:00
|
|
|
# after successful connection outgoing buffer should be empty as syn packet
|
|
|
|
# should be correctly acked
|
|
|
|
sock.numPacketsInOutGoingBuffer() == 0
|
2021-10-19 11:36:57 +00:00
|
|
|
|
|
|
|
server2Called.isSet()
|
|
|
|
|
|
|
|
await utpProt1.closeWait()
|
|
|
|
await utpProt2.closeWait()
|
|
|
|
|
|
|
|
asyncTest "Success data transfer when data fits into one packet":
|
|
|
|
var server1Called = newAsyncEvent()
|
|
|
|
let address = initTAddress("127.0.0.1", 9079)
|
|
|
|
let utpProt1 = UtpProtocol.new(setAcceptedCallback(server1Called), address)
|
|
|
|
|
|
|
|
var serverSocketFut = newFuture[UtpSocket]()
|
|
|
|
let address1 = initTAddress("127.0.0.1", 9080)
|
|
|
|
let utpProt2 = UtpProtocol.new(setIncomingSocketCallback(serverSocketFut), address1)
|
|
|
|
|
|
|
|
let clientSocket = await utpProt1.connectTo(address1)
|
|
|
|
|
|
|
|
# this future will be completed when we called accepted connection callback
|
|
|
|
discard await serverSocketFut
|
|
|
|
|
|
|
|
let serverSocket =
|
|
|
|
try:
|
|
|
|
serverSocketFut.read()
|
|
|
|
except:
|
|
|
|
raiseAssert "Unexpected error when reading finished future"
|
|
|
|
|
|
|
|
check:
|
|
|
|
clientSocket.isConnected()
|
|
|
|
# after successful connection outgoing buffer should be empty as syn packet
|
|
|
|
# should be correctly acked
|
|
|
|
clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
|
|
|
|
# Server socket is not in connected state, until first data transfer
|
|
|
|
(not serverSocket.isConnected())
|
|
|
|
|
|
|
|
let bytesToTransfer = generateByteArray(rng[], 100)
|
|
|
|
|
|
|
|
let bytesReceivedFromClient = await transferData(clientSocket, serverSocket, bytesToTransfer)
|
|
|
|
|
|
|
|
check:
|
|
|
|
bytesToTransfer == bytesReceivedFromClient
|
|
|
|
serverSocket.isConnected()
|
|
|
|
|
|
|
|
let bytesReceivedFromServer = await transferData(serverSocket, clientSocket, bytesToTransfer)
|
|
|
|
|
|
|
|
check:
|
|
|
|
bytesToTransfer == bytesReceivedFromServer
|
|
|
|
|
|
|
|
await utpProt1.closeWait()
|
|
|
|
await utpProt2.closeWait()
|
|
|
|
|
|
|
|
asyncTest "Success data transfer when data need to be sliced into multiple packets":
|
|
|
|
var server1Called = newAsyncEvent()
|
|
|
|
let address = initTAddress("127.0.0.1", 9079)
|
|
|
|
let utpProt1 = UtpProtocol.new(setAcceptedCallback(server1Called), address)
|
|
|
|
|
|
|
|
var serverSocketFut = newFuture[UtpSocket]()
|
|
|
|
let address1 = initTAddress("127.0.0.1", 9080)
|
|
|
|
let utpProt2 = UtpProtocol.new(setIncomingSocketCallback(serverSocketFut), address1)
|
2021-10-11 12:16:06 +00:00
|
|
|
|
2021-10-19 11:36:57 +00:00
|
|
|
let clientSocket = await utpProt1.connectTo(address1)
|
|
|
|
|
|
|
|
# this future will be completed when we called accepted connection callback
|
|
|
|
discard await serverSocketFut
|
|
|
|
|
|
|
|
let serverSocket =
|
|
|
|
try:
|
|
|
|
serverSocketFut.read()
|
|
|
|
except:
|
|
|
|
raiseAssert "Unexpected error when reading finished future"
|
|
|
|
|
|
|
|
check:
|
|
|
|
clientSocket.isConnected()
|
|
|
|
# after successful connection outgoing buffer should be empty as syn packet
|
|
|
|
# should be correctly acked
|
|
|
|
clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
|
|
|
|
(not serverSocket.isConnected())
|
|
|
|
|
|
|
|
# 5000 bytes is over maximal packet size
|
|
|
|
let bytesToTransfer = generateByteArray(rng[], 5000)
|
|
|
|
|
|
|
|
let bytesReceivedFromClient = await transferData(clientSocket, serverSocket, bytesToTransfer)
|
|
|
|
let bytesReceivedFromServer = await transferData(serverSocket, clientSocket, bytesToTransfer)
|
|
|
|
|
|
|
|
# ultimatly all send packets will acked, and outgoing buffer will be empty
|
|
|
|
await waitUntil(proc (): bool = clientSocket.numPacketsInOutGoingBuffer() == 0)
|
|
|
|
await waitUntil(proc (): bool = serverSocket.numPacketsInOutGoingBuffer() == 0)
|
|
|
|
|
|
|
|
check:
|
|
|
|
serverSocket.isConnected()
|
|
|
|
clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
serverSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
bytesReceivedFromClient == bytesToTransfer
|
|
|
|
bytesReceivedFromServer == bytesToTransfer
|
|
|
|
|
|
|
|
await utpProt1.closeWait()
|
|
|
|
await utpProt2.closeWait()
|
|
|
|
|
|
|
|
asyncTest "Success multiple data transfers when data need to be sliced into multiple packets":
|
|
|
|
var server1Called = newAsyncEvent()
|
|
|
|
let address = initTAddress("127.0.0.1", 9079)
|
|
|
|
let utpProt1 = UtpProtocol.new(setAcceptedCallback(server1Called), address)
|
|
|
|
|
|
|
|
var serverSocketFut = newFuture[UtpSocket]()
|
|
|
|
let address1 = initTAddress("127.0.0.1", 9080)
|
|
|
|
let utpProt2 = UtpProtocol.new(setIncomingSocketCallback(serverSocketFut), address1)
|
|
|
|
|
|
|
|
let clientSocket = await utpProt1.connectTo(address1)
|
|
|
|
|
|
|
|
# this future will be completed when we called accepted connection callback
|
|
|
|
discard await serverSocketFut
|
|
|
|
|
|
|
|
let serverSocket =
|
|
|
|
try:
|
|
|
|
serverSocketFut.read()
|
|
|
|
except:
|
|
|
|
raiseAssert "Unexpected error when reading finished future"
|
|
|
|
|
|
|
|
check:
|
|
|
|
clientSocket.isConnected()
|
|
|
|
# after successful connection outgoing buffer should be empty as syn packet
|
|
|
|
# should be correctly acked
|
|
|
|
clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
|
|
|
|
|
|
|
|
# 5000 bytes is over maximal packet size
|
|
|
|
let bytesToTransfer = generateByteArray(rng[], 5000)
|
|
|
|
|
|
|
|
let written = await clientSocket.write(bytesToTransfer)
|
|
|
|
|
|
|
|
check:
|
|
|
|
written == len(bytesToTransfer)
|
|
|
|
|
|
|
|
let bytesToTransfer1 = generateByteArray(rng[], 5000)
|
|
|
|
|
|
|
|
let written1 = await clientSocket.write(bytesToTransfer1)
|
|
|
|
|
|
|
|
check:
|
|
|
|
written1 == len(bytesToTransfer)
|
|
|
|
|
|
|
|
let bytesReceived = await serverSocket.read(len(bytesToTransfer) + len(bytesToTransfer1))
|
|
|
|
|
|
|
|
# ultimatly all send packets will acked, and outgoing buffer will be empty
|
|
|
|
await waitUntil(proc (): bool = clientSocket.numPacketsInOutGoingBuffer() == 0)
|
|
|
|
|
|
|
|
check:
|
|
|
|
clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
bytesToTransfer.concat(bytesToTransfer1) == bytesReceived
|
|
|
|
|
2021-10-11 12:16:06 +00:00
|
|
|
await utpProt1.closeWait()
|
|
|
|
await utpProt2.closeWait()
|