Fix autobahn tls tests (#57)

* split out message and control frames sending

* fix premature closure under TLS

* dissable hints noise
This commit is contained in:
Dmitriy Ryajov 2021-06-11 18:40:56 -06:00 committed by GitHub
parent 3e1599d790
commit 03744f37c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 47 deletions

View File

@ -15,11 +15,11 @@ requires "nimcrypto"
requires "bearssl" requires "bearssl"
task test, "run tests": task test, "run tests":
exec "nim c -r --opt:speed -d:debug --verbosity:0 --hints:off -d:chronicles_log_level=info ./tests/testcommon.nim" exec "nim --hints:off c -r --opt:speed -d:debug --verbosity:0 --hints:off -d:chronicles_log_level=info ./tests/testcommon.nim"
rmFile "./tests/testcommon" rmFile "./tests/testcommon"
exec "nim c -r --opt:speed -d:debug --verbosity:0 --hints:off -d:chronicles_log_level=info ./tests/testwebsockets.nim" exec "nim --hints:off c -r --opt:speed -d:debug --verbosity:0 --hints:off -d:chronicles_log_level=info ./tests/testwebsockets.nim"
rmFile "./tests/testwebsockets" rmFile "./tests/testwebsockets"
exec "nim -d:secure c -r --opt:speed -d:debug --verbosity:0 --hints:off -d:chronicles_log_level=info ./tests/testwebsockets.nim" exec "nim --hints:off -d:secure c -r --opt:speed -d:debug --verbosity:0 --hints:off -d:chronicles_log_level=info ./tests/testwebsockets.nim"
rmFile "./tests/testwebsockets" rmFile "./tests/testwebsockets"

View File

@ -51,9 +51,9 @@ proc closeStream*(stream: AsyncStreamRW) {.async.} =
proc closeWait*(stream: AsyncStream) {.async.} = proc closeWait*(stream: AsyncStream) {.async.} =
await allFutures( await allFutures(
stream.reader.tsource.closeTransp(),
stream.reader.closeStream(), stream.reader.closeStream(),
stream.writer.closeStream()) stream.writer.closeStream(),
stream.reader.tsource.closeTransp())
proc sendResponse*( proc sendResponse*(
request: HttpRequest, request: HttpRequest,

View File

@ -23,47 +23,16 @@ proc prepareCloseBody(code: StatusCodes, reason: string): seq[byte] =
if ord(code) > 999: if ord(code) > 999:
result = @(ord(code).uint16.toBytesBE()) & result result = @(ord(code).uint16.toBytesBE()) & result
proc writeMessage*( proc writeMessage*(ws: WSSession,
ws: WSSession,
data: seq[byte] = @[], data: seq[byte] = @[],
opcode: Opcode, opcode: Opcode,
maskKey: MaskKey,
extensions: seq[Ext]) {.async.} = extensions: seq[Ext]) {.async.} =
## Send a frame applying the supplied
## extensions
##
if ws.readyState == ReadyState.Closed:
raise newException(WSClosedError, "Socket is closed!")
logScope:
opcode = opcode
dataSize = data.len
masked = ws.masked
trace "Sending data to remote"
var maskKey: array[4, char]
if ws.masked:
maskKey = genMaskKey(ws.rng)
if opcode notin {Opcode.Text, Opcode.Cont, Opcode.Binary}: if opcode notin {Opcode.Text, Opcode.Cont, Opcode.Binary}:
warn "Attempting to send a data frame with an invalid opcode!"
if ws.readyState in {ReadyState.Closing} and opcode notin {Opcode.Close}: raise newException(WSInvalidOpcodeError,
return &"Attempting to send a data frame with an invalid opcode {opcode}!")
await ws.stream.writer.write(
(await Frame(
fin: true,
rsv1: false,
rsv2: false,
rsv3: false,
opcode: opcode,
mask: ws.masked,
data: data, # allow sending data with close messages
maskKey: maskKey)
.encode()))
return
let maxSize = ws.frameSize let maxSize = ws.frameSize
var i = 0 var i = 0
@ -86,16 +55,76 @@ proc writeMessage*(
if i >= data.len: if i >= data.len:
break break
proc writeControl*(
ws: WSSession,
data: seq[byte] = @[],
opcode: Opcode,
maskKey: MaskKey) {.async.} =
## Send a frame applying the supplied
## extensions
##
logScope:
opcode = opcode
dataSize = data.len
masked = ws.masked
if opcode in {Opcode.Text, Opcode.Cont, Opcode.Binary}:
warn "Attempting to send a control frame with an invalid opcode!"
raise newException(WSInvalidOpcodeError,
&"Attempting to send a control frame with an invalid opcode {opcode}!")
let frame = Frame(
fin: true,
rsv1: false,
rsv2: false,
rsv3: false,
opcode: opcode,
mask: ws.masked,
data: data,
maskKey: maskKey)
let encoded = await frame.encode()
await ws.stream.writer.write(encoded)
trace "Wrote control frame"
proc send*( proc send*(
ws: WSSession, ws: WSSession,
data: seq[byte] = @[], data: seq[byte] = @[],
opcode: Opcode): Future[void] = opcode: Opcode): Future[void]
{.raises: [Defect, WSClosedError].} =
## Send a frame ## Send a frame
## ##
return ws.writeMessage(data, opcode, ws.extensions) if ws.readyState == ReadyState.Closed:
raise newException(WSClosedError, "WebSocket is closed!")
proc send*(ws: WSSession, data: string): Future[void] = if ws.readyState in {ReadyState.Closing} and opcode notin {Opcode.Close}:
trace "Can only respond with Close opcode to a closing connection"
return
logScope:
opcode = opcode
dataSize = data.len
masked = ws.masked
trace "Sending data to remote"
let maskKey = if ws.masked:
genMaskKey(ws.rng)
else:
default(MaskKey)
if opcode in {Opcode.Text, Opcode.Cont, Opcode.Binary}:
return ws.writeMessage(data, opcode, maskKey, ws.extensions)
return ws.writeControl(data, opcode, maskKey)
proc send*(
ws: WSSession,
data: string): Future[void]
{.raises: [Defect, WSClosedError].} =
send(ws, data.toBytes(), Opcode.Text) send(ws, data.toBytes(), Opcode.Text)
proc handleClose*( proc handleClose*(
@ -169,6 +198,13 @@ proc handleClose*(
await ws.send(prepareCloseBody(code, reason), Opcode.Close) await ws.send(prepareCloseBody(code, reason), Opcode.Close)
ws.readyState = ReadyState.Closed ws.readyState = ReadyState.Closed
# TODO: Under TLS, the response takes longer
# to depart and fails to write the resp code
# and cleanly close the connection. Definitely
# looks like a bug, but not sure if it's chronos
# or us?
await sleepAsync(10.millis)
await ws.stream.closeWait() await ws.stream.closeWait()
proc handleControl*(ws: WSSession, frame: Frame) {.async.} = proc handleControl*(ws: WSSession, frame: Frame) {.async.} =
@ -248,7 +284,10 @@ proc readFrame*(ws: WSSession, extensions: seq[Ext] = @[]): Future[Frame] {.asyn
return frame return frame
proc ping*(ws: WSSession, data: seq[byte] = @[]): Future[void] = proc ping*(
ws: WSSession,
data: seq[byte] = @[]): Future[void]
{.raises: [Defect, WSClosedError].} =
ws.send(data, opcode = Opcode.Ping) ws.send(data, opcode = Opcode.Ping)
proc recv*( proc recv*(
@ -346,9 +385,10 @@ proc recv*(
return consumed return consumed
except CatchableError as exc: except CatchableError as exc:
trace "Exception reading frames", exc = exc.msg
ws.readyState = ReadyState.Closed ws.readyState = ReadyState.Closed
await ws.stream.closeWait() await ws.stream.closeWait()
trace "Exception reading frames", exc = exc.msg
raise exc raise exc
finally: finally:
if not isNil(ws.frame) and (ws.frame.fin and ws.frame.remainder <= 0): if not isNil(ws.frame) and (ws.frame.fin and ws.frame.remainder <= 0):