websocket.nim icon indicating copy to clipboard operation
websocket.nim copied to clipboard

Unable to elevate connection to websocket with jester

Open rokups opened this issue 6 years ago • 0 comments

I have a simple jester webapp that uses a route as websocket. Client connecting to server throws an exception:

~/src/nim/karax-test % ./agent
/home/rk/src/nim/karax-test/agent.nim(11) agent
/home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(311) main
/home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(34) main_continue
/home/rk/src/nim/karax-test/agent.nim(6) mainIter
/home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(1839) waitFor
/home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(1533) poll
/home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(1299) runOnce
/home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(210) processPendingCallbacks
/home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(34) newAsyncWebsocketClient_continue
/home/rk/src/nim/karax-test/websocket/client.nim(92) newAsyncWebsocketClientIter
[[reraised from:
/home/rk/src/nim/karax-test/agent.nim(11) agent
/home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(311) main
/home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(34) main_continue
/home/rk/src/nim/karax-test/agent.nim(6) mainIter
/home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(1841) waitFor
/home/rk/src/nim/Nim/lib/pure/asyncfutures.nim(353) read
]]
[[reraised from:
/home/rk/src/nim/karax-test/agent.nim(11) agent
/home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(1841) waitFor
/home/rk/src/nim/Nim/lib/pure/asyncfutures.nim(353) read
]]
Error: unhandled exception: Server did not reply with a websocket upgrade: 400 Bad Request
Async traceback:
  /home/rk/src/nim/karax-test/agent.nim(11)             agent
  /home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(311)     main
  /home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(34)      main_continue
  /home/rk/src/nim/karax-test/agent.nim(6)              mainIter
  /home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(1839) waitFor
  /home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(1533) poll
  /home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(1299) runOnce
  /home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(210)  processPendingCallbacks
  /home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(34)      newAsyncWebsocketClient_continue
  /home/rk/src/nim/karax-test/websocket/client.nim(92)  newAsyncWebsocketClientIter
  #[
    /home/rk/src/nim/karax-test/agent.nim(11)             agent
    /home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(311)     main
    /home/rk/src/nim/Nim/lib/pure/asyncmacro.nim(34)      main_continue
    /home/rk/src/nim/karax-test/agent.nim(6)              mainIter
    /home/rk/src/nim/Nim/lib/pure/asyncdispatch.nim(1841) waitFor
    /home/rk/src/nim/Nim/lib/pure/asyncfutures.nim(353)   read
  ]#
Exception message: Server did not reply with a websocket upgrade: 400 Bad Request
Exception type: [ProtocolError]

Server log:

INFO Jester is making jokes at http://0.0.0.0:8080
DEBUG GET 
DEBUG   400 Bad Request {"content-type": @["text/html;charset=utf-8"]}

Tested version - latest master. This used to work on v0.3.5.

Server:

import asyncdispatch
import jester
import os
import strutils
import websocket
import httpcore


proc websocket_server(request: Request): Future[tuple[code: HttpCode, msg: string]] {.async.} =
    var ws: tuple[ws: AsyncWebSocket, error: string]
    try:
        ws = await verifyWebsocketRequest(request.getNativeReq(), "app")
    except:
        ws.ws = nil

    if ws.ws.isNil:
        echo "WS negotiation failed: ", ws.error
        result.code = Http400
        result.msg = "Websocket negotiation failed: " & ws.error
        return

    while true:
        var msg: tuple[opcode: Opcode, data: string]
        try:
            msg = await ws.ws.readData()
        except:
            echo getCurrentExceptionMsg()
            return
        try:
            case msg.opcode
            of Opcode.Text:
                await ws.ws.sendText(msg.data)
            of Opcode.Binary:
                await ws.ws.sendBinary(msg.data)
            of Opcode.Close:
                let (closeCode, reason) = extractCloseData(msg.data)
                result.code = Http200
                result.msg = "Websocket closed"
                return
            else: 
                discard
        except:
            echo "Websocket exception: ", getCurrentExceptionMsg()

router app_router:
    get "/ws":
        let (code, msg) = await websocket_server(request)
        resp code, msg
    
    get "/":
        resp Http200, readFile("public/index.html")
    

proc main() =
    var port: Port
    try:
        port = param_str(1).parse_int().Port
    except:
        port = 8080.Port
    let settings = new_settings(port=port)
    var jester = init_jester(app_router, settings=settings)

    jester.serve()

when is_main_module:
    main()

Client:

import asyncdispatch
import websocket


proc main() {.async.} =
    let ws: AsyncWebSocket = waitFor newAsyncWebsocketClient("localhost", Port(8080), path = "/ws", protocols = @["app"])
    await ws.sendText("ping")
    await ws.close()

when is_main_module:
    waitFor main()

rokups avatar Jul 26 '19 14:07 rokups