add test cases for multiple extensions flow

using dummy `base64` and `hex` extensions
- check flow client-server flow,
  `hex` to `base64` and `base64` to `hex`
- check for encode-decode roundtrip
This commit is contained in:
jangko 2021-06-16 17:44:38 +07:00
parent a96a123bfe
commit 32b62989a4
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
4 changed files with 313 additions and 0 deletions

View File

@ -0,0 +1,120 @@
## nim-ws
## Copyright (c) 2021 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/[strutils],
pkg/[stew/results,
stew/base64,
chronos,
chronicles],
../../ws/types,
../../ws/frame
type
Base64Ext = ref object of Ext
padding: bool
transform: bool
const
extID = "base64"
method decode(ext: Base64Ext, frame: Frame): Future[Frame] {.async.} =
if frame.opcode notin {Opcode.Text, Opcode.Binary, Opcode.Cont}:
return frame
if frame.opcode in {Opcode.Text, Opcode.Binary}:
ext.transform = frame.rsv2
frame.rsv2 = false
if not ext.transform:
return frame
if frame.length > 0:
var data: seq[byte]
var buf: array[0xFFFF, byte]
while data.len < frame.length.int:
let len = min(frame.length.int - data.len, buf.len)
let read = await frame.read(ext.session.stream.reader, addr buf[0], len)
data.add toOpenArray(buf, 0, read - 1)
if data.len > ext.session.frameSize:
raise newException(WSPayloadTooLarge, "payload exceeds allowed max frame size")
# bug in Base64.Decode when accepts seq[byte]
let instr = cast[string](data)
if ext.padding:
frame.data = Base64Pad.decode(instr)
else:
frame.data = Base64.decode(instr)
trace "Base64Ext decode", input=frame.length, output=frame.data.len
frame.length = frame.data.len.uint64
frame.offset = 0
frame.consumed = 0
frame.mask = false
return frame
method encode(ext: Base64Ext, frame: Frame): Future[Frame] {.async.} =
if frame.opcode notin {Opcode.Text, Opcode.Binary, Opcode.Cont}:
return frame
if frame.opcode in {Opcode.Text, Opcode.Binary}:
ext.transform = true
frame.rsv2 = ext.transform
if not ext.transform:
return frame
frame.length = frame.data.len.uint64
if ext.padding:
frame.data = cast[seq[byte]](Base64Pad.encode(frame.data))
else:
frame.data = cast[seq[byte]](Base64.encode(frame.data))
trace "Base64Ext encode", input=frame.length, output=frame.data.len
frame.length = frame.data.len.uint64
frame.offset = 0
frame.consumed = 0
return frame
method toHttpOptions(ext: Base64Ext): string =
extID & "; pad=" & $ext.padding
proc base64Factory*(padding: bool): ExtFactory =
proc factory(isServer: bool,
args: seq[ExtParam]): Result[Ext, string] {.
gcsafe, raises: [Defect].} =
# you can capture configuration variables via closure
# if you want
var ext = Base64Ext(
name : extID,
transform: false
)
for arg in args:
if arg.name == "pad":
ext.padding = arg.value == "true"
break
ok(ext)
ExtFactory(
name: extID,
factory: factory,
clientOffer: extID & "; pad=" & $padding
)

103
tests/extensions/hexext.nim Normal file
View File

@ -0,0 +1,103 @@
## nim-ws
## Copyright (c) 2021 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/[strutils],
pkg/[stew/results,
stew/byteutils,
chronos,
chronicles],
../../ws/types,
../../ws/frame
type
HexExt = ref object of Ext
transform: bool
const
extID = "hex"
method decode(ext: HexExt, frame: Frame): Future[Frame] {.async.} =
if frame.opcode notin {Opcode.Text, Opcode.Binary, Opcode.Cont}:
return frame
if frame.opcode in {Opcode.Text, Opcode.Binary}:
ext.transform = frame.rsv3
frame.rsv3 = false
if not ext.transform:
return frame
if frame.length > 0:
var data: seq[byte]
var buf: array[0xFFFF, byte]
while data.len < frame.length.int:
let len = min(frame.length.int - data.len, buf.len)
let read = await frame.read(ext.session.stream.reader, addr buf[0], len)
data.add toOpenArray(buf, 0, read - 1)
if data.len > ext.session.frameSize:
raise newException(WSPayloadTooLarge, "payload exceeds allowed max frame size")
frame.data = hexToSeqByte(cast[string](data))
trace "HexExt decode", input=frame.length, output=frame.data.len
frame.length = frame.data.len.uint64
frame.offset = 0
frame.consumed = 0
frame.mask = false
return frame
method encode(ext: HexExt, frame: Frame): Future[Frame] {.async.} =
if frame.opcode notin {Opcode.Text, Opcode.Binary, Opcode.Cont}:
return frame
if frame.opcode in {Opcode.Text, Opcode.Binary}:
ext.transform = true
frame.rsv3 = ext.transform
if not ext.transform:
return frame
frame.length = frame.data.len.uint64
frame.data = cast[seq[byte]](toHex(frame.data))
trace "HexExt encode", input=frame.length, output=frame.data.len
frame.length = frame.data.len.uint64
frame.offset = 0
frame.consumed = 0
return frame
method toHttpOptions(ext: HexExt): string =
extID
proc hexFactory*(): ExtFactory =
proc factory(isServer: bool,
args: seq[ExtParam]): Result[Ext, string] {.
gcsafe, raises: [Defect].} =
# you can capture configuration variables via closure
# if you want
var ext = HexExt(
name : extID,
transform: false
)
ok(ext)
ExtFactory(
name: extID,
factory: factory,
clientOffer: extID
)

View File

@ -0,0 +1,89 @@
## nim-ws
## Copyright (c) 2021 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 pkg/[chronos, stew/byteutils]
import ../asyncunit
import ./base64ext, ./hexext
import ../../ws/ws, ../helpers
suite "UTF-8 validator in action":
var server: HttpServer
let address = initTAddress("127.0.0.1:8888")
let hexFactory = hexFactory()
let base64Factory = base64Factory(padding = true)
teardown:
server.stop()
await server.closeWait()
test "hex to base64 ext flow":
let testData = "hello world"
proc handle(request: HttpRequest) {.async.} =
let server = WSServer.new(
protos = ["proto"],
factories = [hexFactory, base64Factory],
)
let ws = await server.handleRequest(request)
let recvData = await ws.recv()
await ws.send(recvData,
if ws.binary: Opcode.Binary else: Opcode.Text)
await waitForClose(ws)
server = HttpServer.create(
address,
handle,
flags = {ReuseAddr})
server.start()
let client = await WebSocket.connect(
host = "127.0.0.1",
port = Port(8888),
path = "/ws",
protocols = @["proto"],
factories = @[hexFactory, base64Factory]
)
await client.send(testData)
let res = await client.recv()
check testData.toBytes() == res
await client.close()
test "base64 to hex ext flow":
let testData = "hello world"
proc handle(request: HttpRequest) {.async.} =
let server = WSServer.new(
protos = ["proto"],
factories = [hexFactory, base64Factory],
)
let ws = await server.handleRequest(request)
let recvData = await ws.recv()
await ws.send(recvData,
if ws.binary: Opcode.Binary else: Opcode.Text)
await waitForClose(ws)
server = HttpServer.create(
address,
handle,
flags = {ReuseAddr})
server.start()
let client = await WebSocket.connect(
host = "127.0.0.1",
port = Port(8888),
path = "/ws",
protocols = @["proto"],
factories = @[base64Factory, hexFactory]
)
await client.send(testData)
let res = await client.recv()
check testData.toBytes() == res
await client.close()

View File

@ -3,3 +3,4 @@
import ./testframes
import ./testutf8
import ./testextutils
import ./extensions/testexts