nim-websock/tests/extensions/testcompression.nim
Etan Kissling 7b2ed397d6
properly support concurrent message sending (#126)
`nim-websock` suffered from a number of issues that are being addressed:

1. Long messages > `frameSize` (default 1 MB) were split into fragments
   of `frameSize` each. However, when a concurrent message is sent, it
   may be interleaved among the fragments of an already-sending message.
   This is only allowed for control packets without a mux extension.

2. When the WebSocket session is closed, a msg may have been partially
   received. This partial frame was reported as a full message, without
   indication that the receiving was canceled. This behaviour is fixed
   by raising a `WSClosedError` instead of reporting the partial msg.

3. When an individual `send` operation was canceled, it would actually
   stop sending the remainder of a potentially partially sent messages.
   This would corrupt the stream for concurrent and followup operations.
   Cancellation is now inhibited for the message currently sending.
   It is still possible to cancel messages that are not yet scheduled.

4. Messages could get reordered when using asynchronous encoders. This
   is addressed by delaying followup messages until the current message
   is fully encoded and transmitted (except for control packets).

Co-authored-by: Tanguy <tanguy@status.im>
2022-09-06 11:09:21 +02:00

110 lines
3.0 KiB
Nim

## nim-websock
## Copyright (c) 2021-2022 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/[os, strutils]
import pkg/[chronos/unittest2/asynctests, stew/io2]
import ../../websock/websock
import ../../websock/extensions/compression/deflate
const
dataFolder = currentSourcePath.rsplit(os.DirSep, 1)[0] / "data"
suite "permessage deflate compression":
setup:
var server: HttpServer
let address = initTAddress("127.0.0.1:8888")
let deflateFactory = deflateFactory()
teardown:
if server != nil:
server.stop()
waitFor server.closeWait()
asyncTest "text compression":
let textData = io2.readAllBytes(dataFolder / "alice29.txt").get()
proc handle(request: HttpRequest) {.async.} =
let server = WSServer.new(
protos = ["proto"],
factories = [deflateFactory],
)
let ws = await server.handleRequest(request)
while ws.readyState != ReadyState.Closed:
let recvData = await ws.recvMsg()
if ws.readyState == ReadyState.Closed:
break
await ws.send(recvData,
if ws.binary: Opcode.Binary else: Opcode.Text)
server = HttpServer.create(
address,
handle,
flags = {ReuseAddr})
server.start()
let client = await WebSocket.connect(
host = "127.0.0.1:8888",
path = "/ws",
protocols = @["proto"],
factories = @[deflateFactory]
)
await client.send(textData, Opcode.Text)
var recvData: seq[byte]
while recvData.len < textData.len:
let res = await client.recvMsg()
recvData.add res
if client.readyState == ReadyState.Closed:
break
check textData == recvData
await client.close()
asyncTest "binary data compression":
let binaryData = io2.readAllBytes(dataFolder / "fireworks.jpg").get()
proc handle(request: HttpRequest) {.async.} =
let server = WSServer.new(
protos = ["proto"],
factories = [deflateFactory],
)
let ws = await server.handleRequest(request)
while ws.readyState != ReadyState.Closed:
let recvData = await ws.recvMsg()
if ws.readyState == ReadyState.Closed:
break
await ws.send(recvData,
if ws.binary: Opcode.Binary else: Opcode.Text)
server = HttpServer.create(
address,
handle,
flags = {ReuseAddr})
server.start()
let client = await WebSocket.connect(
host = "127.0.0.1:8888",
path = "/ws",
protocols = @["proto"],
factories = @[deflateFactory]
)
await client.send(binaryData, Opcode.Binary)
var recvData: seq[byte]
while recvData.len < binaryData.len:
let res = await client.recvMsg()
recvData.add res
if client.readyState == ReadyState.Closed:
break
check binaryData == recvData
await client.close()