## 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/byteutils, asynctest, chronos, chronicles ], ../ws/[ws, utf8_dfa] suite "UTF-8 DFA validator": test "single octet": check: validateUTF8("\x01") validateUTF8("\x32") validateUTF8("\x7f") validateUTF8("\x80") == false test "two octets": check: validateUTF8("\xc2\x80") validateUTF8("\xc4\x80") validateUTF8("\xdf\xbf") validateUTF8("\xdfu\xc0") == false validateUTF8("\xdf") == false test "three octets": check: validateUTF8("\xe0\xa0\x80") validateUTF8("\xe1\x80\x80") validateUTF8("\xef\xbf\xbf") validateUTF8("\xef\xbf\xc0") == false validateUTF8("\xef\xbf") == false test "four octets": check: validateUTF8("\xf0\x90\x80\x80") validateUTF8("\xf0\x92\x80\x80") validateUTF8("\xf0\x9f\xbf\xbf") validateUTF8("\xf0\x9f\xbf\xc0") == false validateUTF8("\xf0\x9f\xbf") == false test "overlong sequence": check: validateUTF8("\xc0\xaf") == false validateUTF8("\xe0\x80\xaf") == false validateUTF8("\xf0\x80\x80\xaf") == false validateUTF8("\xf8\x80\x80\x80\xaf") == false validateUTF8("\xfc\x80\x80\x80\x80\xaf") == false test "max overlong sequence": check: validateUTF8("\xc1\xbf") == false validateUTF8("\xe0\x9f\xbf") == false validateUTF8("\xf0\x8f\xbf\xbf") == false validateUTF8("\xf8\x87\xbf\xbf\xbf") == false validateUTF8("\xfc\x83\xbf\xbf\xbf\xbf") == false test "distinct codepoint": check: validateUTF8("foobar") validateUTF8("foob\xc3\xa6r") validateUTF8("foob\xf0\x9f\x99\x88r") proc waitForClose(ws: WSSession) {.async.} = try: while ws.readystate != ReadyState.Closed: discard await ws.recv() except CatchableError: debug "Closing websocket" # TODO: use new test framework from dryajov # if it is ready. var server: HttpServer let address = initTAddress("127.0.0.1:8888") suite "UTF-8 validator in action": teardown: server.stop() await server.closeWait() test "valid UTF-8 sequence": let testData = "hello world" proc handle(request: HttpRequest) {.async.} = check request.uri.path == "/ws" let server = WSServer.new(protos = ["proto"]) let ws = await server.handleRequest(request) let res = await ws.recv() check: string.fromBytes(res) == testData ws.binary == false await waitForClose(ws) server = HttpServer.create( address, handle, flags = {ReuseAddr}) server.start() let session = await WebSocket.connect( "127.0.0.1", Port(8888), path = "/ws", protocols = @["proto"], ) await session.send(testData) await session.close() test "valid UTF-8 sequence in close reason": let testData = "hello world" let closeReason = "i want to close" proc handle(request: HttpRequest) {.async.} = check request.uri.path == "/ws" proc onClose(status: Status, reason: string): CloseResult {.gcsafe, raises: [Defect].} = try: check status == Status.Fulfilled check reason == closeReason return (status, reason) except Exception as exc: raise newException(Defect, exc.msg) let server = WSServer.new(protos = ["proto"], onClose = onClose) let ws = await server.handleRequest(request) let res = await ws.recv() check: string.fromBytes(res) == testData ws.binary == false await waitForClose(ws) server = HttpServer.create( address, handle, flags = {ReuseAddr}) server.start() let session = await WebSocket.connect( "127.0.0.1", Port(8888), path = "/ws", protocols = @["proto"], ) await session.send(testData) await session.close(reason = closeReason) test "invalid UTF-8 sequence": # TODO: how to check for Invalid UTF8 exception? let testData = "hello world\xc0\xaf" proc handle(request: HttpRequest) {.async.} = check request.uri.path == "/ws" let server = WSServer.new(protos = ["proto"]) let ws = await server.handleRequest(request) discard await ws.recv() server = HttpServer.create( address, handle, flags = {ReuseAddr}) server.start() let session = await WebSocket.connect( "127.0.0.1", Port(8888), path = "/ws", protocols = @["proto"] ) await session.send(testData) await waitForClose( session) check session.readyState == ReadyState.Closed test "invalid UTF-8 sequence close code": # TODO: how to check for Invalid UTF8 exception? let testData = "hello world" let closeReason = "i want to close\xc0\xaf" proc handle(request: HttpRequest) {.async.} = check request.uri.path == "/ws" let server = WSServer.new(protos = ["proto"]) let ws = await server.handleRequest(request) let res = await ws.recv() check: string.fromBytes(res) == testData ws.binary == false server = HttpServer.create( address, handle, flags = {ReuseAddr}) server.start() let session = await WebSocket.connect( "127.0.0.1", Port(8888), path = "/ws", protocols = @["proto"] ) await session.send(testData) await session.close(reason = closeReason) await waitForClose( session) check session.readyState == ReadyState.Closed