nim-websock/tests/testwebsockets.nim

1198 lines
31 KiB
Nim
Raw Normal View History

2021-06-27 06:35:36 +00:00
## nim-websock
## Copyright (c) 2021-2023 Status Research & Development GmbH
## Licensed under either of
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
## at your option.
## This file may not be copied, modified, or distributed except according to
## those terms.
import std/[
random,
sequtils,
strutils]
import pkg/[
httputils,
chronos/unittest2/asynctests,
chronicles,
stew/byteutils]
WIP: Implement websocket TLS. (#7) * Update http to use chronos http. * Implement TLS in websocket. * Add webscoket TLS test. * Minor nit. * Add TLS test file. * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Implement TLS in websocket. * Minor nit. * merge master * wip * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Update http to use chronos http. * Implement TLS in websocket. * Minor nit. * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Implement TLS in websocket. * Minor nit. * add testing keys * wip * fix test * wip * remove eth dep and add skipdirs * fix package structure * fix deps * check nim version * Fix CI failure. * Don't call `ws.stream.closeWait()` * always close both ends to complete the sequence * misc * don't fail on close * Fix windows CI. * fix linux x86 builds * use consistent connect pattern * move keys to better place * return dumbResponse * small cleanup Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com>
2021-04-13 22:05:58 +00:00
2021-06-27 06:35:36 +00:00
import ../websock/websock
import ./helpers
let address = initTAddress("127.0.0.1:8888")
suite "Test handshake":
setup:
var
server: HttpServer
teardown:
if server != nil:
server.stop()
waitFor server.closeWait()
asyncTest "Should not select incorrect protocol":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let
server = WSServer.new(protos = ["proto"])
ws = await server.handleRequest(request)
check ws.proto == ""
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
protocols = @["wrongproto"])
check session.proto == ""
await session.stream.closeWait()
asyncTest "Test for incorrect version":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["ws"])
expect WSVersionError:
discard await server.handleRequest(request)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
expect WSFailedUpgradeError:
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
version = 14)
discard session
asyncTest "Test for client headers":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
check request.headers.getString("Connection").toUpperAscii() ==
"Upgrade".toUpperAscii()
check request.headers.getString("Upgrade").toUpperAscii() ==
"websocket".toUpperAscii()
check request.headers.getString("Cache-Control").toUpperAscii() ==
"no-cache".toUpperAscii()
check request.headers.getString("Sec-WebSocket-Version") == $WSDefaultVersion
check request.headers.contains("Sec-WebSocket-Key")
await request.sendError(Http500)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
expect WSFailedUpgradeError:
discard await connectClient()
asyncTest "Test for incorrect scheme":
let uri = "wx://127.0.0.1:8888/ws"
expect WSWrongUriSchemeError:
discard await WebSocket.connect(
parseUri(uri),
protocols = @["proto"])
suite "Test transmission":
setup:
var
server: HttpServer
teardown:
if server != nil:
server.stop()
waitFor server.closeWait()
asyncTest "Server - asyncTest reading simple frame":
let testString = "Hello!"
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
let servRes = await ws.recvMsg()
check string.fromBytes(servRes) == testString
await ws.waitForClose()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
await session.send(testString)
await session.close()
asyncTest "Send text message message with payload of length 65535":
let testString = rndStr(65535)
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
let servRes = await ws.recvMsg()
check string.fromBytes(servRes) == testString
await ws.waitForClose()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
await session.send(testString)
await session.close()
asyncTest "Client - asyncTest reading simple frame":
let testString = "Hello!"
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
await ws.send(testString)
await ws.close()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
var clientRes = await session.recvMsg()
check string.fromBytes(clientRes) == testString
await waitForClose(session)
WIP: Implement websocket TLS. (#7) * Update http to use chronos http. * Implement TLS in websocket. * Add webscoket TLS test. * Minor nit. * Add TLS test file. * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Implement TLS in websocket. * Minor nit. * merge master * wip * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Update http to use chronos http. * Implement TLS in websocket. * Minor nit. * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Implement TLS in websocket. * Minor nit. * add testing keys * wip * fix test * wip * remove eth dep and add skipdirs * fix package structure * fix deps * check nim version * Fix CI failure. * Don't call `ws.stream.closeWait()` * always close both ends to complete the sequence * misc * don't fail on close * Fix windows CI. * fix linux x86 builds * use consistent connect pattern * move keys to better place * return dumbResponse * small cleanup Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com>
2021-04-13 22:05:58 +00:00
asyncTest "Close handle cancellation":
let testString = "Hello!"
let cancelSignal = newFuture[void]()
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
let servRes = await ws.recvMsg()
check string.fromBytes(servRes) == testString
await ws.waitForClose()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
proc client() {.async, gcsafe.} =
let session = await connectClient()
await session.send(testString)
cancelSignal.complete()
expect CancelledError:
await session.close()
let task = client()
await cancelSignal
await task.cancelAndWait()
suite "Test ping-pong":
setup:
var
server: HttpServer
teardown:
if server != nil:
server.stop()
waitFor server.closeWait()
asyncTest "Server - asyncTest ping-pong control messages":
var ping, pong = false
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(
protos = ["proto"],
onPong = proc(data: openArray[byte]) =
pong = true
)
let ws = await server.handleRequest(request)
await ws.ping()
await ws.close()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
onPing = proc(data: openArray[byte]) =
ping = true
)
await waitForClose(session)
check:
ping
pong
asyncTest "Client - asyncTest ping-pong control messages":
var ping, pong = false
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(
protos = ["proto"],
onPing = proc(data: openArray[byte]) =
ping = true
)
let ws = await server.handleRequest(request)
await waitForClose(ws)
check:
ping
pong
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
onPong = proc(data: openArray[byte]) =
pong = true
)
await session.ping()
await session.close()
asyncTest "Send ping with small text payload":
let testData = toBytes("Hello, world!")
var ping, pong = false
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(
protos = ["proto"],
2021-05-22 09:04:40 +00:00
onPing = proc(data: openArray[byte]) =
ping = data == testData)
let ws = await server.handleRequest(request)
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
2021-05-22 09:04:40 +00:00
onPong = proc(data: openArray[byte]) =
pong = true
)
await session.ping(testData)
await session.close()
check:
ping
pong
asyncTest "Test ping payload message length":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
expect WSPayloadTooLarge:
discard await ws.recvMsg()
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let str = rndStr(126)
let session = await connectClient()
await session.ping(str.toBytes())
await session.close()
suite "Test framing":
setup:
var
server: HttpServer
teardown:
if server != nil:
server.stop()
waitFor server.closeWait()
asyncTest "should split message into frames":
let testString = "1234567890"
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
let frame1 = await ws.readFrame(@[])
check not isNil(frame1)
var data1 = newSeq[byte](frame1.remainder().int)
let read1 = await ws.stream.reader.readOnce(addr data1[0], data1.len)
check read1 == 5
let frame2 = await ws.readFrame(@[])
check not isNil(frame2)
var data2 = newSeq[byte](frame2.remainder().int)
let read2 = await ws.stream.reader.readOnce(addr data2[0], data2.len)
check read2 == 5
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
frameSize = 5)
await session.send(testString)
await session.close()
asyncTest "should fail to read past max message size":
let testString = "1234567890"
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
await ws.send(testString)
await ws.close()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
WIP: Implement websocket TLS. (#7) * Update http to use chronos http. * Implement TLS in websocket. * Add webscoket TLS test. * Minor nit. * Add TLS test file. * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Implement TLS in websocket. * Minor nit. * merge master * wip * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Update http to use chronos http. * Implement TLS in websocket. * Minor nit. * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Implement TLS in websocket. * Minor nit. * add testing keys * wip * fix test * wip * remove eth dep and add skipdirs * fix package structure * fix deps * check nim version * Fix CI failure. * Don't call `ws.stream.closeWait()` * always close both ends to complete the sequence * misc * don't fail on close * Fix windows CI. * fix linux x86 builds * use consistent connect pattern * move keys to better place * return dumbResponse * small cleanup Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com>
2021-04-13 22:05:58 +00:00
expect WSMaxMessageSizeError:
discard await session.recvMsg(5)
await waitForClose(session)
asyncTest "should serialize long messages":
const numMessages = 10
let testData = newSeqWith(10 * 1024 * 1024, byte.rand())
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
for i in 0 ..< numMessages:
try:
let message = await ws.recvMsg()
let matchesExpectedMessage = (message == testData)
check matchesExpectedMessage
except CatchableError:
fail()
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
frameSize = 1 * 1024 * 1024)
var futs: seq[Future[void]]
for i in 0 ..< numMessages:
futs.add session.send(testData, Opcode.Binary)
await allFutures(futs)
await session.close()
asyncTest "should handle cancellations":
const numMessages = 10
let expectedNumMessages = numMessages - 1
let testData = newSeqWith(10 * 1024 * 1024, byte.rand())
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
for i in 0 ..< expectedNumMessages:
try:
let message = await ws.recvMsg()
let matchesExpectedMessage = (message == testData)
check matchesExpectedMessage
except CatchableError:
fail()
expect WSClosedError:
discard await ws.recvMsg() # try to receive canceled message
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
frameSize = 1 * 1024 * 1024)
var futs: seq[Future[void]]
for i in 0 ..< numMessages:
futs.add session.send(testData, Opcode.Binary)
futs[0].cancelSoon() # expected to complete as it already started sending
futs[^2].cancelSoon() # expected to be canceled as it has not started yet
await allFutures(futs)
await session.close()
asyncTest "should prioritize control packets":
const numMessages = 10
let testData = newSeqWith(10 * 1024 * 1024, byte.rand())
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
expect WSClosedError:
discard await ws.recvMsg()
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
frameSize = 1 * 1024 * 1024)
let messageFut = session.send(testData, Opcode.Binary)
# interleave ping packets
var futs: seq[Future[void]]
for i in 0 ..< numMessages:
futs.add session.send(opcode = Opcode.Ping)
await allFutures(futs)
check not messageFut.finished
# interleave close packet
await session.close()
check messageFut.finished
await messageFut
suite "Test Closing":
setup:
var
server: HttpServer
teardown:
if server != nil:
server.stop()
waitFor server.closeWait()
asyncTest "Server closing":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
await ws.close()
WIP: Implement websocket TLS. (#7) * Update http to use chronos http. * Implement TLS in websocket. * Add webscoket TLS test. * Minor nit. * Add TLS test file. * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Implement TLS in websocket. * Minor nit. * merge master * wip * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Update http to use chronos http. * Implement TLS in websocket. * Minor nit. * Update http to use chronos http. (#6) * Update http to use chronos http. * Add stream.nim file. * Address comments. * Fix CI failure. * Minor change. * Address comments. * Fix windows CI failing test. * minor cleanup * spacess * more idiomatic connect * use stew/base10 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> * Implement TLS in websocket. * Minor nit. * add testing keys * wip * fix test * wip * remove eth dep and add skipdirs * fix package structure * fix deps * check nim version * Fix CI failure. * Don't call `ws.stream.closeWait()` * always close both ends to complete the sequence * misc * don't fail on close * Fix windows CI. * fix linux x86 builds * use consistent connect pattern * move keys to better place * return dumbResponse * small cleanup Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com>
2021-04-13 22:05:58 +00:00
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
await waitForClose(session)
check session.readyState == ReadyState.Closed
asyncTest "Server closing with status":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
proc closeServer(status: StatusCodes, reason: string): CloseResult{.gcsafe,
raises: [].} =
try:
check status == StatusTooLarge
check reason == "Message too big!"
except Exception as exc:
raise newException(Defect, exc.msg)
return (StatusFulfilled, "")
let server = WSServer.new(
protos = ["proto"],
onClose = closeServer
)
let ws = await server.handleRequest(request)
await ws.close()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
proc clientClose(status: StatusCodes, reason: string): CloseResult {.gcsafe,
raises: [].} =
try:
check status == StatusFulfilled
return (StatusTooLarge, "Message too big!")
except Exception as exc:
raise newException(Defect, exc.msg)
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
onClose = clientClose)
await waitForClose(session)
check session.readyState == ReadyState.Closed
asyncTest "Client closing":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
await session.close()
asyncTest "Client closing with status":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
proc closeServer(status: StatusCodes, reason: string): CloseResult{.gcsafe,
raises: [].} =
try:
check status == StatusFulfilled
return (StatusTooLarge, "Message too big!")
except Exception as exc:
raise newException(Defect, exc.msg)
let server = WSServer.new(
protos = ["proto"],
onClose = closeServer
)
let ws = await server.handleRequest(request)
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
proc clientClose(status: StatusCodes, reason: string): CloseResult {.gcsafe,
raises: [].} =
try:
check status == StatusTooLarge
check reason == "Message too big!"
return (StatusFulfilled, "")
except Exception as exc:
raise newException(Defect, exc.msg)
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
onClose = clientClose)
await session.close()
check session.readyState == ReadyState.Closed
asyncTest "Mutual closing":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
await ws.close()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
await session.close()
await waitForClose(session)
check session.readyState == ReadyState.Closed
asyncTest "Server closing with valid close code 3999":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
await ws.close(code = StatusCodes(StatusLibsCodes.high))
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
proc closeClient(status: StatusCodes, reason: string): CloseResult
{.gcsafe, raises: [].} =
try:
check status == StatusCodes(StatusLibsCodes.high)
return (StatusCodes(StatusLibsCodes.high), "Reserved StatusCodes")
except Exception as exc:
raise newException(Defect, exc.msg)
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
onClose = closeClient)
await waitForClose(session)
asyncTest "Client closing with valid close code 3999":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
proc closeServer(status: StatusCodes, reason: string): CloseResult{.gcsafe,
raises: [].} =
try:
check status == StatusCodes(3999)
return (StatusCodes(3999), "Reserved StatusCodes")
except Exception as exc:
raise newException(Defect, exc.msg)
let server = WSServer.new(
protos = ["proto"],
onClose = closeServer
)
let ws = await server.handleRequest(request)
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
await session.close(code = StatusCodes(3999))
asyncTest "Server closing with Payload of length 2":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
# Close with payload of length 2
await ws.close(reason = "HH")
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
await waitForClose(session)
asyncTest "Client closing with Payload of length 2":
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
# Close with payload of length 2
await session.close(reason = "HH")
2021-05-22 09:04:40 +00:00
suite "Test Payload":
setup:
let rng {.used.} = HmacDrbgContext.new()
var
server: HttpServer
teardown:
if server != nil:
server.stop()
waitFor server.closeWait()
asyncTest "Test payload of length 0":
let emptyStr = ""
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
let servRes = await ws.recvMsg()
check:
servRes.len == 0
string.fromBytes(servRes) == emptyStr
await ws.send(emptyStr)
await ws.waitForClose()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
await session.send(emptyStr)
let clientRes = await session.recvMsg()
check:
clientRes.len == 0
string.fromBytes(clientRes) == emptyStr
await session.close()
asyncTest "Test multiple payloads of length 0":
let emptyStr = ""
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
for _ in 0..<3:
let servRes = await ws.recvMsg()
check:
servRes.len == 0
string.fromBytes(servRes) == emptyStr
for i in 0..3:
await ws.send(emptyStr)
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient()
for i in 0..3:
await session.send(emptyStr)
for _ in 0..<3:
let clientRes = await session.recvMsg()
check:
clientRes.len == 0
string.fromBytes(clientRes) == emptyStr
await session.close()
asyncTest "Send two fragments":
let testString = "1234567890"
let msg = toBytes(testString)
let maxFrameSize = 5
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
let respData = await ws.recvMsg()
check:
string.fromBytes(respData) == testString
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
frameSize = maxFrameSize)
let maskKey = MaskKey.random(rng[])
await session.stream.writer.write(
(await Frame(
fin: false,
rsv1: false,
rsv2: false,
rsv3: false,
opcode: Opcode.Text,
mask: true,
data: msg[0..4],
maskKey: maskKey)
.encode()))
await session.stream.writer.write(
(await Frame(
fin: true,
rsv1: false,
rsv2: false,
rsv3: false,
opcode: Opcode.Cont,
mask: true,
data: msg[5..9],
maskKey: maskKey)
.encode()))
await session.close()
asyncTest "Send two fragments with a ping with payload in-between":
var ping, pong = false
let testString = "1234567890"
let msg = toBytes(testString)
let maxFrameSize = 5
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(
protos = ["proto"],
onPing = proc(data: openArray[byte]) =
ping = true
)
let ws = await server.handleRequest(request)
let respData = await ws.recvMsg()
check:
string.fromBytes(respData) == testString
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
frameSize = maxFrameSize,
2021-05-22 09:04:40 +00:00
onPong = proc(data: openArray[byte]) =
pong = true
)
let maskKey = MaskKey.random(rng[])
await session.stream.writer.write(
(await Frame(
fin: false,
rsv1: false,
rsv2: false,
rsv3: false,
opcode: Opcode.Text,
mask: true,
data: msg[0..4],
maskKey: maskKey)
.encode()))
await session.ping()
await session.stream.writer.write(
(await Frame(
fin: true,
rsv1: false,
rsv2: false,
rsv3: false,
opcode: Opcode.Cont,
mask: true,
data: msg[5..9],
maskKey: maskKey)
.encode()))
await session.close()
check:
ping
pong
asyncTest "Send text message with multiple frames":
const FrameSize = 3000
let testData = rndStr(FrameSize * 3 + 100)
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"], frameSize = FrameSize)
let ws = await server.handleRequest(request)
let res = await ws.recvMsg()
check ws.binary == false
await ws.send(res, Opcode.Text)
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let ws = await connectClient(
address = address,
frameSize = FrameSize
)
await ws.send(testData)
let echoed = await ws.recvMsg()
await ws.close()
check:
string.fromBytes(echoed) == testData
ws.binary == false
2021-04-15 08:00:52 +00:00
suite "Test Binary message with Payload":
setup:
var
server: HttpServer
2021-04-15 08:00:52 +00:00
teardown:
if server != nil:
server.stop()
waitFor server.closeWait()
2021-04-15 08:00:52 +00:00
asyncTest "Test binary message with single empty payload message":
2021-04-15 08:00:52 +00:00
let emptyData = newSeq[byte](0)
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
let servRes = await ws.recvMsg()
2021-05-22 09:04:40 +00:00
2021-04-15 08:00:52 +00:00
check:
servRes == emptyData
2021-05-22 09:04:40 +00:00
ws.binary == true
2021-04-15 08:00:52 +00:00
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
2021-04-15 08:00:52 +00:00
let session = await connectClient()
await session.send(emptyData, Opcode.Binary)
await session.close()
2021-04-15 08:00:52 +00:00
asyncTest "Test binary message with multiple empty payload":
2021-04-15 08:00:52 +00:00
let emptyData = newSeq[byte](0)
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
let servRes = await ws.recvMsg()
2021-05-22 09:04:40 +00:00
2021-04-15 08:00:52 +00:00
check:
servRes == emptyData
2021-05-22 09:04:40 +00:00
ws.binary == true
2021-04-15 08:00:52 +00:00
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
2021-04-15 08:00:52 +00:00
let session = await connectClient()
2021-04-15 08:00:52 +00:00
for i in 0..3:
await session.send(emptyData, Opcode.Binary)
await session.close()
2021-04-15 08:00:52 +00:00
asyncTest "Send binary data with small text payload":
2021-04-15 08:00:52 +00:00
let testData = rndBin(10)
trace "testData", testData = testData
var
ping = false
pong = false
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(
protos = ["proto"],
2021-05-22 09:04:40 +00:00
onPing = proc(data: openArray[byte]) =
ping = true
2021-04-15 08:00:52 +00:00
)
let ws = await server.handleRequest(request)
2021-05-22 09:04:40 +00:00
let res = await ws.recvMsg()
2021-04-15 08:00:52 +00:00
check:
res == testData
2021-05-22 09:04:40 +00:00
ws.binary == true
2021-04-15 08:00:52 +00:00
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
2021-04-15 08:00:52 +00:00
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
2021-05-22 09:04:40 +00:00
onPong = proc(data: openArray[byte]) =
pong = true
2021-04-15 08:00:52 +00:00
)
await session.send(testData, Opcode.Binary)
await session.close()
2021-04-15 08:00:52 +00:00
asyncTest "Send binary message message with payload of length 125":
2021-04-15 08:00:52 +00:00
let testData = rndBin(125)
var ping, pong = false
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(
protos = ["proto"],
2021-05-22 09:04:40 +00:00
onPing = proc(data: openArray[byte]) =
ping = true
2021-04-15 08:00:52 +00:00
)
let ws = await server.handleRequest(request)
2021-05-22 09:04:40 +00:00
let res = await ws.recvMsg()
2021-04-15 08:00:52 +00:00
check:
res == testData
2021-05-22 09:04:40 +00:00
ws.binary == true
2021-04-15 08:00:52 +00:00
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
2021-04-15 08:00:52 +00:00
let session = await connectClient(
address = initTAddress("127.0.0.1:8888"),
2021-05-22 09:04:40 +00:00
onPong = proc(data: openArray[byte]) =
pong = true
2021-04-15 08:00:52 +00:00
)
await session.send(testData, Opcode.Binary)
await session.close()
2021-04-15 08:00:52 +00:00
asyncTest "Send binary message with multiple frames":
const FrameSize = 3000
let testData = rndBin(FrameSize * 3)
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let server = WSServer.new(protos = ["proto"])
let ws = await server.handleRequest(request)
let res = await ws.recvMsg()
check:
ws.binary == true
res == testData
await ws.send(res, Opcode.Binary)
await waitForClose(ws)
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let ws = await connectClient(
address = address,
frameSize = FrameSize
)
await ws.send(testData, Opcode.Binary)
let echoed = await ws.recvMsg()
check:
echoed == testData
await ws.close()
check:
echoed == testData
ws.binary == true
suite "Partial frames":
setup:
var
server: HttpServer
proc lowLevelRecv(
senderFrameSize, receiverFrameSize, readChunkSize: int) {.async.} =
const
howMuchWood = "How much wood could a wood chuck chuck ..."
proc handle(request: HttpRequest) {.async.} =
check request.uri.path == WSPath
let
server = WSServer.new(frameSize = receiverFrameSize)
ws = await server.handleRequest(request)
var
res = newSeq[byte](howMuchWood.len)
pos = 0
while ws.readyState != ReadyState.Closed:
let read = await ws.recv(addr res[pos], min(res.len - pos, readChunkSize))
pos += read
if pos >= res.len:
break
res.setLen(pos)
check res.len == howMuchWood.toBytes().len
check res == howMuchWood.toBytes()
await ws.waitForClose()
server = createServer(
address = address,
handler = handle,
flags = {ReuseAddr})
let session = await connectClient(
address = address,
frameSize = senderFrameSize)
await session.send(howMuchWood)
await session.close()
teardown:
if server != nil:
server.stop()
waitFor server.closeWait()
asyncTest "read in chunks less than sender frameSize":
await lowLevelRecv(7, 7, 5)
asyncTest "read in chunks greater than sender frameSize":
await lowLevelRecv(3, 7, 5)
asyncTest "sender frameSize greater than receiver":
await lowLevelRecv(7, 5, 5)
asyncTest "receiver frameSize greater than sender":
await lowLevelRecv(7, 10, 5)