2024-01-16 17:10:26 +00:00
|
|
|
# Copyright (c) 2020-2024 Status Research & Development GmbH
|
2021-10-11 12:16:06 +00:00
|
|
|
# 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,
|
2022-06-17 20:45:37 +00:00
|
|
|
chronos,
|
2021-10-11 12:16:06 +00:00
|
|
|
testutils/unittests,
|
2021-11-04 06:38:46 +00:00
|
|
|
./test_utils,
|
2022-11-10 08:01:58 +00:00
|
|
|
../../eth/keys,
|
2024-01-16 17:10:26 +00:00
|
|
|
../../eth/utp/[utp_router, utp_protocol],
|
2022-11-10 08:01:58 +00:00
|
|
|
../stubloglevel
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
proc setAcceptedCallback(
|
|
|
|
event: AsyncEvent
|
|
|
|
): AcceptConnectionCallback[TransportAddress] =
|
2021-10-28 09:41:43 +00:00
|
|
|
return (
|
|
|
|
proc(server: UtpRouter[TransportAddress], client: UtpSocket[TransportAddress]): Future[void] =
|
|
|
|
let fut = newFuture[void]()
|
|
|
|
event.fire()
|
|
|
|
fut.complete()
|
|
|
|
fut
|
|
|
|
)
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
proc registerIncomingSocketCallback(
|
|
|
|
serverSockets: AsyncQueue
|
|
|
|
): AcceptConnectionCallback[TransportAddress] =
|
2021-10-28 09:41:43 +00:00
|
|
|
return (
|
|
|
|
proc(server: UtpRouter[TransportAddress], client: UtpSocket[TransportAddress]): Future[void] =
|
|
|
|
serverSockets.addLast(client)
|
|
|
|
)
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
proc allowOneIdCallback(
|
|
|
|
allowedId: uint16
|
|
|
|
): AllowConnectionCallback[TransportAddress] =
|
2021-11-18 09:05:56 +00:00
|
|
|
return (
|
|
|
|
proc(r: UtpRouter[TransportAddress], remoteAddress: TransportAddress, connectionId: uint16): bool =
|
|
|
|
connectionId == allowedId
|
|
|
|
)
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
proc transferData(
|
|
|
|
sender: UtpSocket[TransportAddress],
|
|
|
|
receiver: UtpSocket[TransportAddress],
|
|
|
|
data: seq[byte]
|
|
|
|
): Future[seq[byte]] {.async.} =
|
2021-10-19 11:36:57 +00:00
|
|
|
let bytesWritten = await sender.write(data)
|
2024-01-16 17:10:26 +00:00
|
|
|
check:
|
|
|
|
bytesWritten.isOk()
|
|
|
|
bytesWritten.value() == len(data)
|
2021-10-19 11:36:57 +00:00
|
|
|
let received = await receiver.read(len(data))
|
|
|
|
return received
|
|
|
|
|
2021-12-20 12:14:50 +00:00
|
|
|
type
|
2021-10-28 09:41:43 +00:00
|
|
|
ClientServerScenario = object
|
|
|
|
utp1: UtpProtocol
|
|
|
|
utp2: UtpProtocol
|
|
|
|
clientSocket: UtpSocket[TransportAddress]
|
|
|
|
serverSocket: UtpSocket[TransportAddress]
|
|
|
|
|
|
|
|
TwoClientsServerScenario = object
|
|
|
|
utp1: UtpProtocol
|
|
|
|
utp2: UtpProtocol
|
|
|
|
utp3: UtpProtocol
|
2024-01-16 17:10:26 +00:00
|
|
|
client1Socket: UtpSocket[TransportAddress]
|
|
|
|
client2Socket: UtpSocket[TransportAddress]
|
2021-10-28 09:41:43 +00:00
|
|
|
serverSocket1: UtpSocket[TransportAddress]
|
|
|
|
serverSocket2: UtpSocket[TransportAddress]
|
|
|
|
|
|
|
|
proc initClientServerScenario(): Future[ClientServerScenario] {.async.} =
|
2024-01-16 17:10:26 +00:00
|
|
|
let
|
|
|
|
server1Incoming = newAsyncEvent() # Not used
|
|
|
|
address1 = initTAddress("127.0.0.1", 9079)
|
|
|
|
utpProto1 = UtpProtocol.new(setAcceptedCallback(server1Incoming), address1)
|
2021-10-28 09:41:43 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
utpProto2Sockets = newAsyncQueue[UtpSocket[TransportAddress]]()
|
|
|
|
address2 = initTAddress("127.0.0.1", 9080)
|
|
|
|
utpProto2 = UtpProtocol.new(registerIncomingSocketCallback(utpProto2Sockets), address2)
|
|
|
|
|
|
|
|
clientSocket = await utpProto1.connectTo(address2)
|
|
|
|
serverSocket = await utpProto2Sockets.popFirst()
|
2021-10-28 09:41:43 +00:00
|
|
|
|
|
|
|
return ClientServerScenario(
|
2024-01-16 17:10:26 +00:00
|
|
|
utp1: utpProto1,
|
|
|
|
utp2: utpProto2,
|
2021-11-18 09:05:56 +00:00
|
|
|
clientSocket: clientSocket.get(),
|
2021-10-28 09:41:43 +00:00
|
|
|
serverSocket: serverSocket
|
|
|
|
)
|
|
|
|
|
|
|
|
proc close(s: ClientServerScenario) {.async.} =
|
2021-11-09 14:29:59 +00:00
|
|
|
await s.clientSocket.destroyWait()
|
|
|
|
await s.serverSocket.destroyWait()
|
|
|
|
await s.utp1.shutdownWait()
|
|
|
|
await s.utp2.shutdownWait()
|
2021-10-28 09:41:43 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
proc initTwoClientsOneServerScenario(): Future[TwoClientsServerScenario] {.async.} =
|
|
|
|
let
|
|
|
|
server1Incoming = newAsyncEvent() # not used
|
|
|
|
address1 = initTAddress("127.0.0.1", 9079)
|
|
|
|
utpProto1 = UtpProtocol.new(setAcceptedCallback(server1Incoming), address1)
|
2021-10-28 09:41:43 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
server2Incoming = newAsyncEvent() # not used
|
|
|
|
address2 = initTAddress("127.0.0.1", 9080)
|
|
|
|
utpProto2 = UtpProtocol.new(setAcceptedCallback(server2Incoming), address2)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
server3Sockets = newAsyncQueue[UtpSocket[TransportAddress]]()
|
|
|
|
address3 = initTAddress("127.0.0.1", 9081)
|
|
|
|
utpProto3 = UtpProtocol.new(registerIncomingSocketCallback(server3Sockets), address3)
|
2021-10-28 09:41:43 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
client1Socket = await utpProto1.connectTo(address3)
|
|
|
|
client2Socket = await utpProto2.connectTo(address3)
|
2021-10-28 09:41:43 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
await waitUntil(proc (): bool = len(server3Sockets) == 2)
|
2021-10-28 09:41:43 +00:00
|
|
|
|
|
|
|
return TwoClientsServerScenario(
|
2024-01-16 17:10:26 +00:00
|
|
|
utp1: utpProto1,
|
|
|
|
utp2: utpProto2,
|
|
|
|
utp3: utpProto3,
|
|
|
|
client1Socket: client1Socket.get(),
|
|
|
|
client2Socket: client2Socket.get(),
|
|
|
|
serverSocket1: server3Sockets[0],
|
|
|
|
serverSocket2: server3Sockets[1]
|
2021-10-28 09:41:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
proc close(s: TwoClientsServerScenario) {.async.} =
|
2021-11-09 14:29:59 +00:00
|
|
|
await s.utp1.shutdownWait()
|
|
|
|
await s.utp2.shutdownWait()
|
|
|
|
await s.utp3.shutdownWait()
|
2021-10-28 09:41:43 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
procSuite "uTP over UDP protocol tests":
|
2021-10-11 12:16:06 +00:00
|
|
|
let rng = newRng()
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Connect to remote host: test connection callback":
|
|
|
|
let
|
|
|
|
server1Incoming = newAsyncEvent()
|
|
|
|
address1 = initTAddress("127.0.0.1", 9079)
|
|
|
|
utpProto1 = UtpProtocol.new(setAcceptedCallback(server1Incoming), address1)
|
2021-10-11 12:16:06 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
server2Incoming = newAsyncEvent()
|
|
|
|
address2 = initTAddress("127.0.0.1", 9080)
|
|
|
|
utpProto2 = UtpProtocol.new(setAcceptedCallback(server2Incoming), address2)
|
2021-10-11 12:16:06 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
socketResult = await utpProto1.connectTo(address2)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
check socketResult.isOk()
|
|
|
|
let socket = socketResult.value()
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
# This future will complete when the accepted connection callback is called
|
|
|
|
await server2Incoming.wait()
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
check:
|
|
|
|
socket.isConnected()
|
|
|
|
# after a successful connection the outgoing buffer should be empty as
|
|
|
|
# the SYN packet should have been acked
|
|
|
|
socket.numPacketsInOutGoingBuffer() == 0
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
server2Incoming.isSet()
|
2022-08-12 13:09:59 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
await utpProto1.shutdownWait()
|
|
|
|
await utpProto2.shutdownWait()
|
2022-08-12 13:09:59 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Connect to remote host: test udata pointer and use it in callback":
|
|
|
|
proc cbUserData(
|
|
|
|
server: UtpRouter[TransportAddress],
|
|
|
|
client: UtpSocket[TransportAddress]): Future[void] =
|
|
|
|
let q = getUserData[TransportAddress, AsyncQueue[UtpSocket[TransportAddress]]](server)
|
|
|
|
q.addLast(client)
|
2022-08-12 13:09:59 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let
|
|
|
|
incomingConnections1 = newAsyncQueue[UtpSocket[TransportAddress]]()
|
|
|
|
address1 = initTAddress("127.0.0.1", 9079)
|
|
|
|
utpProto1 = UtpProtocol.new(cbUserData, address1, incomingConnections1)
|
2022-08-12 13:09:59 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
incomingConnections2 = newAsyncQueue[UtpSocket[TransportAddress]]()
|
|
|
|
address2 = initTAddress("127.0.0.1", 9080)
|
|
|
|
utpProto2 = UtpProtocol.new(cbUserData, address2, incomingConnections2)
|
2022-08-12 13:09:59 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
socketResult = await utpProto1.connectTo(address2)
|
|
|
|
|
|
|
|
check socketResult.isOk()
|
2022-08-12 13:09:59 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let clientSocket = socketResult.get()
|
|
|
|
# This future will complete when the accepted connection callback is called
|
|
|
|
let serverSocket = await incomingConnections2.get()
|
2022-08-12 13:09:59 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
clientSocket.isConnected()
|
2024-01-16 17:10:26 +00:00
|
|
|
# after a successful connection the outgoing buffer should be empty as
|
|
|
|
# the SYN packet should have been acked
|
2022-08-12 13:09:59 +00:00
|
|
|
clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
# Server socket is not in connected state until first data transfer
|
2022-08-12 13:09:59 +00:00
|
|
|
not serverSocket.isConnected()
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
await utpProto1.shutdownWait()
|
|
|
|
await utpProto2.shutdownWait()
|
2022-08-12 13:09:59 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Connect to offline remote server host":
|
|
|
|
let
|
|
|
|
server1Incoming = newAsyncEvent()
|
|
|
|
address1 = initTAddress("127.0.0.1", 9079)
|
|
|
|
utpProto1 = UtpProtocol.new(
|
|
|
|
setAcceptedCallback(server1Incoming), address1 , nil,
|
|
|
|
SocketConfig.init(milliseconds(200)))
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
address2 = initTAddress("127.0.0.1", 9080)
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
socketResult = await utpProto1.connectTo(address2)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
check socketResult.isErr()
|
|
|
|
let connectionError = socketResult.error()
|
|
|
|
check connectionError.kind == ConnectionTimedOut
|
2021-11-18 09:05:56 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
await waitUntil(proc (): bool = utpProto1.openSockets() == 0)
|
2021-11-18 09:05:56 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
check utpProto1.openSockets() == 0
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
await utpProto1.shutdownWait()
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Connect to remote host which was initially offline":
|
|
|
|
let
|
|
|
|
server1Incoming = newAsyncEvent()
|
|
|
|
address1 = initTAddress("127.0.0.1", 9079)
|
|
|
|
utpProto1 = UtpProtocol.new(
|
|
|
|
setAcceptedCallback(server1Incoming), address1, nil,
|
|
|
|
# Sets initial SYN timeout to 500ms
|
|
|
|
SocketConfig.init(milliseconds(500)))
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
address2 = initTAddress("127.0.0.1", 9080)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
futSock = utpProto1.connectTo(address2)
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2022-11-16 16:44:00 +00:00
|
|
|
# waiting 400 millisecond will trigger at least one re-send
|
2021-10-25 07:58:13 +00:00
|
|
|
await sleepAsync(milliseconds(400))
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
var server2Incoming = newAsyncEvent()
|
|
|
|
let utpProto2 = UtpProtocol.new(setAcceptedCallback(server2Incoming), address2)
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
# This future will complete when the accepted connection callback is called
|
|
|
|
await server2Incoming.wait()
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-12 08:49:06 +00:00
|
|
|
discard (await futSock)
|
2021-10-25 07:58:13 +00:00
|
|
|
|
|
|
|
check:
|
2021-12-20 12:14:50 +00:00
|
|
|
futSock.finished() and (not futSock.failed()) and (not futSock.cancelled())
|
2024-01-16 17:10:26 +00:00
|
|
|
server2Incoming.isSet()
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
await utpProto1.shutdownWait()
|
|
|
|
await utpProto2.shutdownWait()
|
2021-10-25 07:58:13 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Data transfer where data fits into one packet":
|
2021-10-28 09:41:43 +00:00
|
|
|
let s = await initClientServerScenario()
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2021-10-19 11:36:57 +00:00
|
|
|
check:
|
2021-10-28 09:41:43 +00:00
|
|
|
s.clientSocket.isConnected()
|
2024-01-16 17:10:26 +00:00
|
|
|
# after a successful connection the outgoing buffer should be empty as
|
|
|
|
# the SYN packet should have been acked
|
2021-10-28 09:41:43 +00:00
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
2024-01-16 17:10:26 +00:00
|
|
|
# Server socket is not in connected state until first data transfer
|
2021-10-28 09:41:43 +00:00
|
|
|
(not s.serverSocket.isConnected())
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2022-06-17 20:45:37 +00:00
|
|
|
let bytesToTransfer = rng[].generateBytes(100)
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let bytesReceivedFromClient = await transferData(
|
|
|
|
s.clientSocket, s.serverSocket, bytesToTransfer)
|
2021-10-19 11:36:57 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
bytesToTransfer == bytesReceivedFromClient
|
2021-10-28 09:41:43 +00:00
|
|
|
s.serverSocket.isConnected()
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let bytesReceivedFromServer = await transferData(
|
|
|
|
s.serverSocket, s.clientSocket, bytesToTransfer)
|
2021-10-19 11:36:57 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
bytesToTransfer == bytesReceivedFromServer
|
|
|
|
|
2021-10-28 09:41:43 +00:00
|
|
|
await s.close()
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Data transfer where data need to be sliced into multiple packets":
|
2021-10-28 09:41:43 +00:00
|
|
|
let s = await initClientServerScenario()
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2021-10-19 11:36:57 +00:00
|
|
|
check:
|
2021-10-28 09:41:43 +00:00
|
|
|
s.clientSocket.isConnected()
|
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
(not s.serverSocket.isConnected())
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-12 16:35:44 +00:00
|
|
|
# 20_000 bytes is way over maximal packet size
|
|
|
|
let bytesToTransfer = rng[].generateBytes(20_000)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let bytesReceivedFromClient = await transferData(
|
|
|
|
s.clientSocket, s.serverSocket, bytesToTransfer)
|
|
|
|
let bytesReceivedFromServer = await transferData(
|
|
|
|
s.serverSocket, s.clientSocket, bytesToTransfer)
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2022-11-16 16:44:00 +00:00
|
|
|
# ultimately all send packets will acked, and outgoing buffer will be empty
|
2021-10-28 09:41:43 +00:00
|
|
|
await waitUntil(proc (): bool = s.clientSocket.numPacketsInOutGoingBuffer() == 0)
|
|
|
|
await waitUntil(proc (): bool = s.serverSocket.numPacketsInOutGoingBuffer() == 0)
|
2021-10-19 11:36:57 +00:00
|
|
|
|
|
|
|
check:
|
2021-10-28 09:41:43 +00:00
|
|
|
s.serverSocket.isConnected()
|
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
s.serverSocket.numPacketsInOutGoingBuffer() == 0
|
2021-10-19 11:36:57 +00:00
|
|
|
bytesReceivedFromClient == bytesToTransfer
|
|
|
|
bytesReceivedFromServer == bytesToTransfer
|
|
|
|
|
2021-10-28 09:41:43 +00:00
|
|
|
await s.close()
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Multiple data transfers where data need to be sliced into multiple packets":
|
2021-10-28 09:41:43 +00:00
|
|
|
let s = await initClientServerScenario()
|
2021-10-19 11:36:57 +00:00
|
|
|
|
|
|
|
check:
|
2021-10-28 09:41:43 +00:00
|
|
|
s.clientSocket.isConnected()
|
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
const
|
2024-01-17 21:45:20 +00:00
|
|
|
amountOfTransfers = 3
|
2024-01-16 17:10:26 +00:00
|
|
|
amountOfBytes = 5000
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
var totalBytesToTransfer: seq[byte]
|
2024-01-17 21:45:20 +00:00
|
|
|
for i in 0..<amountOfTransfers:
|
2024-01-16 17:10:26 +00:00
|
|
|
let bytesToTransfer = rng[].generateBytes(amountOfBytes)
|
|
|
|
let written = await s.clientSocket.write(bytesToTransfer)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
check:
|
|
|
|
written.isOk()
|
|
|
|
written.value() == amountOfBytes
|
2021-10-19 11:36:57 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
totalBytesToTransfer.add(bytesToTransfer)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-17 21:45:20 +00:00
|
|
|
let bytesReceived = await s.serverSocket.read(amountOfBytes * amountOfTransfers)
|
2021-10-28 09:41:43 +00:00
|
|
|
await waitUntil(proc (): bool = s.clientSocket.numPacketsInOutGoingBuffer() == 0)
|
2021-10-19 11:36:57 +00:00
|
|
|
|
|
|
|
check:
|
2021-10-28 09:41:43 +00:00
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
2024-01-16 17:10:26 +00:00
|
|
|
totalBytesToTransfer == bytesReceived
|
2021-10-28 09:41:43 +00:00
|
|
|
|
|
|
|
await s.close()
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Data transfers from multiple clients to one server":
|
|
|
|
let s = await initTwoClientsOneServerScenario()
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2021-10-28 09:41:43 +00:00
|
|
|
check:
|
2024-01-16 17:10:26 +00:00
|
|
|
s.client1Socket.isConnected()
|
|
|
|
s.client2Socket.isConnected()
|
|
|
|
s.client1Socket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
s.client2Socket.numPacketsInOutGoingBuffer() == 0
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let
|
|
|
|
numBytesToTransfer = 5000
|
|
|
|
client1Data = rng[].generateBytes(numBytesToTransfer)
|
|
|
|
client2Data = rng[].generateBytes(numBytesToTransfer)
|
2021-10-28 09:41:43 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
discard s.client1Socket.write(client1Data)
|
|
|
|
discard s.client2Socket.write(client2Data)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let serverReadBytes1 = await s.serverSocket1.read(numBytesToTransfer)
|
|
|
|
let serverReadBytes2 = await s.serverSocket2.read(numBytesToTransfer)
|
2021-10-28 09:41:43 +00:00
|
|
|
|
|
|
|
check:
|
2024-01-16 17:10:26 +00:00
|
|
|
client1Data == serverReadBytes1
|
|
|
|
client2Data == serverReadBytes2
|
2021-10-28 09:41:43 +00:00
|
|
|
|
|
|
|
await s.close()
|
2021-11-09 14:29:59 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Graceful stop of the socket":
|
2021-11-09 14:29:59 +00:00
|
|
|
let s = await initClientServerScenario()
|
|
|
|
check:
|
|
|
|
s.clientSocket.isConnected()
|
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
(not s.serverSocket.isConnected())
|
|
|
|
|
2022-06-17 20:45:37 +00:00
|
|
|
let bytesToTransfer = rng[].generateBytes(100)
|
2021-11-09 14:29:59 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let bytesReceivedFromClient = await transferData(
|
|
|
|
s.clientSocket, s.serverSocket, bytesToTransfer)
|
2021-11-09 14:29:59 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
bytesToTransfer == bytesReceivedFromClient
|
|
|
|
s.serverSocket.isConnected()
|
|
|
|
|
|
|
|
await s.clientSocket.closeWait()
|
|
|
|
|
|
|
|
check:
|
|
|
|
not s.clientSocket.isConnected()
|
|
|
|
s.serverSocket.atEof()
|
|
|
|
s.utp1.openSockets() == 0
|
|
|
|
s.utp2.openSockets() == 1
|
|
|
|
|
|
|
|
await s.serverSocket.destroyWait()
|
|
|
|
|
|
|
|
check:
|
|
|
|
not s.serverSocket.isConnected()
|
|
|
|
s.utp2.openSockets() == 0
|
|
|
|
|
|
|
|
await s.close()
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Read data until eof":
|
2021-11-09 14:29:59 +00:00
|
|
|
let s = await initClientServerScenario()
|
|
|
|
check:
|
|
|
|
s.clientSocket.isConnected()
|
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
(not s.serverSocket.isConnected())
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let
|
|
|
|
bytesToTransfer1 = rng[].generateBytes(1000)
|
|
|
|
bytesToTransfer2 = rng[].generateBytes(1000)
|
|
|
|
bytesToTransfer3 = rng[].generateBytes(1000)
|
2021-11-09 14:29:59 +00:00
|
|
|
|
2023-10-29 04:06:48 +00:00
|
|
|
discard await s.clientSocket.write(bytesToTransfer1)
|
|
|
|
discard await s.clientSocket.write(bytesToTransfer2)
|
|
|
|
discard await s.clientSocket.write(bytesToTransfer3)
|
2021-11-09 14:29:59 +00:00
|
|
|
await s.clientSocket.closeWait()
|
|
|
|
|
|
|
|
let readData = await s.serverSocket.read()
|
|
|
|
|
|
|
|
check:
|
|
|
|
readData == concat(bytesToTransfer1, bytesToTransfer2, bytesToTransfer3)
|
|
|
|
s.serverSocket.atEof()
|
|
|
|
s.utp1.openSockets() == 0
|
|
|
|
|
|
|
|
await s.close()
|
2021-11-18 09:05:56 +00:00
|
|
|
|
|
|
|
asyncTest "Accept connection only from allowed peers":
|
2024-01-16 17:10:26 +00:00
|
|
|
const
|
|
|
|
allowedId: uint16 = 10
|
|
|
|
lowSynTimeout = milliseconds(500)
|
|
|
|
|
|
|
|
let
|
|
|
|
server1Incoming = newAsyncEvent() # not used
|
|
|
|
address1 = initTAddress("127.0.0.1", 9079)
|
|
|
|
utpProto1 =
|
|
|
|
UtpProtocol.new(
|
|
|
|
setAcceptedCallback(server1Incoming), address1, nil,
|
|
|
|
SocketConfig.init(lowSynTimeout)
|
|
|
|
)
|
|
|
|
|
|
|
|
server2Incoming = newAsyncEvent() # not used
|
|
|
|
address2 = initTAddress("127.0.0.1", 9080)
|
|
|
|
utpProto2 =
|
|
|
|
UtpProtocol.new(
|
|
|
|
setAcceptedCallback(server2Incoming), address2, nil,
|
|
|
|
SocketConfig.init(lowSynTimeout)
|
|
|
|
)
|
|
|
|
|
|
|
|
server3Sockets = newAsyncQueue[UtpSocket[TransportAddress]]()
|
|
|
|
address3 = initTAddress("127.0.0.1", 9081)
|
|
|
|
utpProto3 =
|
|
|
|
UtpProtocol.new(
|
|
|
|
registerIncomingSocketCallback(server3Sockets),
|
|
|
|
address3,
|
|
|
|
nil,
|
|
|
|
SocketConfig.init(),
|
|
|
|
allowOneIdCallback(allowedId)
|
|
|
|
)
|
|
|
|
|
|
|
|
let allowedSocketRes = await utpProto1.connectTo(address3, allowedId)
|
|
|
|
let notAllowedSocketRes = await utpProto2.connectTo(address3, allowedId + 1)
|
2021-11-18 09:05:56 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
allowedSocketRes.isOk()
|
|
|
|
notAllowedSocketRes.isErr()
|
2024-01-16 17:10:26 +00:00
|
|
|
# remote did not allow this connection and it timed out
|
2021-11-18 09:05:56 +00:00
|
|
|
notAllowedSocketRes.error().kind == ConnectionTimedOut
|
|
|
|
|
|
|
|
let clientSocket = allowedSocketRes.get()
|
2024-01-16 17:10:26 +00:00
|
|
|
let serverSocket = await server3Sockets.get()
|
2021-11-18 09:05:56 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
clientSocket.connectionId() == allowedId
|
|
|
|
serverSocket.connectionId() == allowedId
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
await utpProto1.shutdownWait()
|
|
|
|
await utpProto2.shutdownWait()
|
|
|
|
await utpProto3.shutdownWait()
|
2021-12-09 09:52:21 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Data transfer of a lot of data should increase window on sender side":
|
2021-12-09 09:52:21 +00:00
|
|
|
let s = await initClientServerScenario()
|
2022-04-04 11:44:32 +00:00
|
|
|
let startMaxWindow = 2 * s.clientSocket.getSocketConfig().payloadSize
|
2024-01-16 17:10:26 +00:00
|
|
|
|
2021-12-09 09:52:21 +00:00
|
|
|
check:
|
|
|
|
s.clientSocket.isConnected()
|
2024-01-16 17:10:26 +00:00
|
|
|
# initially the window has value equal to a pre-configured constant
|
2021-12-09 09:52:21 +00:00
|
|
|
s.clientSocket.currentMaxWindowSize == startMaxWindow
|
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
|
|
|
|
(not s.serverSocket.isConnected())
|
|
|
|
|
|
|
|
# big transfer of 50kb
|
2022-06-17 20:45:37 +00:00
|
|
|
let bytesToTransfer = rng[].generateBytes(50000)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let bytesReceivedFromClient = await transferData(
|
|
|
|
s.clientSocket, s.serverSocket, bytesToTransfer)
|
2021-12-09 09:52:21 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
# ultimately all send packets will be acked and the outgoing buffer will be empty
|
2021-12-09 09:52:21 +00:00
|
|
|
await waitUntil(proc (): bool = s.clientSocket.numPacketsInOutGoingBuffer() == 0)
|
|
|
|
|
|
|
|
check:
|
2024-01-16 17:10:26 +00:00
|
|
|
# we can only assess that the window has grown, because the specific value
|
|
|
|
# depends on particular timings
|
2021-12-09 09:52:21 +00:00
|
|
|
s.clientSocket.currentMaxWindowSize > startMaxWindow
|
|
|
|
s.serverSocket.isConnected()
|
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
bytesReceivedFromClient == bytesToTransfer
|
|
|
|
|
|
|
|
await s.close()
|
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
asyncTest "Unused socket should decay its max send window":
|
2021-12-09 09:52:21 +00:00
|
|
|
let s = await initClientServerScenario()
|
2022-04-04 11:44:32 +00:00
|
|
|
let startMaxWindow = 2 * s.clientSocket.getSocketConfig().payloadSize
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2021-12-09 09:52:21 +00:00
|
|
|
check:
|
|
|
|
s.clientSocket.isConnected()
|
2024-01-16 17:10:26 +00:00
|
|
|
# initially the window has value equal to a pre-configured constant
|
2021-12-09 09:52:21 +00:00
|
|
|
s.clientSocket.currentMaxWindowSize == startMaxWindow
|
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
|
|
|
|
(not s.serverSocket.isConnected())
|
|
|
|
|
|
|
|
# big transfer of 50kb
|
2022-06-17 20:45:37 +00:00
|
|
|
let bytesToTransfer = rng[].generateBytes(50000)
|
2021-12-20 12:14:50 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
let bytesReceivedFromClient = await transferData(
|
|
|
|
s.clientSocket, s.serverSocket, bytesToTransfer)
|
2021-12-09 09:52:21 +00:00
|
|
|
|
2024-01-16 17:10:26 +00:00
|
|
|
# ultimately all send packets will be acked and the outgoing buffer will be empty
|
2021-12-09 09:52:21 +00:00
|
|
|
await waitUntil(proc (): bool = s.clientSocket.numPacketsInOutGoingBuffer() == 0)
|
|
|
|
|
|
|
|
let maximumMaxWindow = s.clientSocket.currentMaxWindowSize
|
|
|
|
|
|
|
|
check:
|
2024-01-16 17:10:26 +00:00
|
|
|
# we can only assess that the window has grown, because the specific value
|
|
|
|
# depends on particular timings
|
2021-12-09 09:52:21 +00:00
|
|
|
maximumMaxWindow > startMaxWindow
|
|
|
|
s.serverSocket.isConnected()
|
|
|
|
s.clientSocket.numPacketsInOutGoingBuffer() == 0
|
|
|
|
bytesReceivedFromClient == bytesToTransfer
|
|
|
|
|
|
|
|
# wait long enough to trigger timeout
|
|
|
|
await sleepAsync(seconds(5))
|
|
|
|
|
|
|
|
check:
|
|
|
|
# window should decay when idle
|
|
|
|
s.clientSocket.currentMaxWindowSize < maximumMaxWindow
|
|
|
|
|
|
|
|
await s.close()
|
2024-01-17 21:45:20 +00:00
|
|
|
|
|
|
|
asyncTest "Data transfer over multiple sockets":
|
|
|
|
const
|
|
|
|
amountOfTransfers = 100
|
|
|
|
dataToSend: seq[byte] = repeat(byte 0xA0, 1_000_000)
|
|
|
|
|
|
|
|
var readFutures: seq[Future[void]]
|
|
|
|
|
|
|
|
proc readAndCheck(
|
|
|
|
socket: UtpSocket[TransportAddress],
|
|
|
|
): Future[void] {.async.} =
|
|
|
|
let readData = await socket.read()
|
|
|
|
check:
|
|
|
|
readData == dataToSend
|
|
|
|
socket.atEof()
|
|
|
|
|
|
|
|
proc handleIncomingConnection(
|
|
|
|
server: UtpRouter[TransportAddress],
|
|
|
|
client: UtpSocket[TransportAddress]
|
|
|
|
): Future[void] =
|
|
|
|
readFutures.add(client.readAndCheck())
|
|
|
|
|
|
|
|
var fut = newFuture[void]("test.AcceptConnectionCallback")
|
|
|
|
fut.complete()
|
|
|
|
return fut
|
|
|
|
|
|
|
|
proc handleIncomingConnectionDummy(
|
|
|
|
server: UtpRouter[TransportAddress],
|
|
|
|
client: UtpSocket[TransportAddress]
|
|
|
|
): Future[void] =
|
|
|
|
var fut = newFuture[void]("test.AcceptConnectionCallback")
|
|
|
|
fut.complete()
|
|
|
|
return fut
|
|
|
|
|
|
|
|
let
|
|
|
|
address1 = initTAddress("127.0.0.1", 9079)
|
|
|
|
utpProto1 = UtpProtocol.new(handleIncomingConnectionDummy, address1)
|
|
|
|
address2 = initTAddress("127.0.0.1", 9080)
|
|
|
|
utpProto2 = UtpProtocol.new(handleIncomingConnection, address2)
|
|
|
|
|
|
|
|
proc connectSendAndCheck(
|
|
|
|
utpProto: UtpProtocol,
|
|
|
|
address: TransportAddress
|
|
|
|
): Future[void] {.async.} =
|
|
|
|
let socketRes = await utpProto.connectTo(address)
|
|
|
|
check:
|
|
|
|
socketRes.isOk()
|
|
|
|
let socket = socketRes.value()
|
|
|
|
let dataSend = await socket.write(dataToSend)
|
|
|
|
check:
|
|
|
|
dataSend.isOk()
|
|
|
|
dataSend.value() == dataToSend.len()
|
|
|
|
|
|
|
|
await socket.closeWait()
|
|
|
|
|
|
|
|
let t0 = Moment.now()
|
|
|
|
for i in 0..<amountOfTransfers:
|
|
|
|
asyncSpawn utpProto1.connectSendAndCheck(address2)
|
|
|
|
|
|
|
|
while readFutures.len() < amountOfTransfers:
|
|
|
|
await sleepAsync(milliseconds(100))
|
|
|
|
|
|
|
|
await allFutures(readFutures)
|
|
|
|
let elapsed = Moment.now() - t0
|
|
|
|
|
|
|
|
await utpProto1.shutdownWait()
|
|
|
|
await utpProto2.shutdownWait()
|
|
|
|
|
|
|
|
let megabitsSent = amountOfTransfers * dataToSend.len() * 8 / 1_000_000
|
|
|
|
let seconds = float(elapsed.nanoseconds) / 1_000_000_000
|
|
|
|
let throughput = megabitsSent / seconds
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
echo "Sent ", amountOfTransfers, " asynchronous uTP transfers in ", seconds,
|
|
|
|
" seconds, payload throughput: ", throughput, " Mbit/s"
|