Giter Site home page Giter Site logo

websocket.nim's Introduction

websocket.nim

A websocket library for nim, providing both client and server support.

This is a pre-release and not final software. There might be bugs, unintended side-effects, the API might change without warning between releases, and features are missing - namely:

  • A robust RNG for the client data masking

See the API docs for usage instructions.

websocket.nim's People

Contributors

andrewgpu avatar araq avatar ba0f3 avatar dom96 avatar enthus1ast avatar hiteshjasani avatar luked99 avatar metagn avatar nitely avatar niv avatar singularperturbation avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

websocket.nim's Issues

Unable to elevate connection to websocket with jester

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()

reqPing makes code not gcsafe, fails to compile

If I try to compile a simple example which uses readData() using nim 0.17.2, it fails with:

Error: type mismatch: got (AsyncHttpServer, Port, proc (req: Request): Future[system.void]{.locks: <unknown>.})
but expected one of:    
proc serve(server: AsyncHttpServer; port: Port;       
          callback: proc (request: Request): Future[void]; address = ""): Future[void]

The problem seems to be the use of reqPing, which is a global variable and makes the async function not thread safe.

Here's the code I'm using (call to verifyWebsocketRequest() omitted for clarity).

var server = newAsyncHttpServer()

proc cb(req: Request) {.async.} =
    var f = await req.client.readData(false)
    echo $f

waitfor server.serve(Port(8080), cb)

Server did not reply with a websocket upgrade: 400 Bad Request

I've used a discord nim library and when I run the file I get this error

lib code: https://github.com/Krognol/discordnim/blob/master/src/discord.nim#L403

Hint: operation successful (96991 lines compiled; 84.867 sec total; 282.539MiB peakmem; Debug Build) [SuccessX]
Hint: C:\Users\Rizwan\Desktop\Projects\Nim!\test.exe  [Exec]
Server did not reply with a websocket upgrade: 400 Bad Request
Async traceback:
  C:\Users\Rizwan\Desktop\Projects\Nim!\test.nim(14)                                test
  C:\Users\Rizwan\.choosenim\toolchains\nim-0.20.0\lib\pure\asyncdispatch.nim(1839) waitFor
  C:\Users\Rizwan\.choosenim\toolchains\nim-0.20.0\lib\pure\asyncdispatch.nim(1533) poll
  C:\Users\Rizwan\.choosenim\toolchains\nim-0.20.0\lib\pure\asyncdispatch.nim(373)  runOnce
  C:\Users\Rizwan\.choosenim\toolchains\nim-0.20.0\lib\pure\asyncdispatch.nim(210)  processPendingCallbacks
  C:\Users\Rizwan\.choosenim\toolchains\nim-0.20.0\lib\pure\asyncmacro.nim(34)      newAsyncWebsocketClient_continue
  C:\Users\Rizwan\.nimble\pkgs\websocket-0.3.6\websocket\client.nim(95)             newAsyncWebsocketClientIter
Exception message: Server did not reply with a websocket upgrade: 400 Bad Request
Exception type:

example doesn't work

nim c -o:app_server examples/serverexample.nim
nim c -o:app_client examples/clientexample.nim
# in terminal tab 1:
./app_server
# in terminal tab 2:
./app_client

shows for terminal tab 2:

connected!
ping
(opcode: Cont, data: 5af1f03d7063d34100000001)
ping
(opcode: Cont, data: 5af1f0437063d34100000002)

but nothing is shown for tab 1
(I'd expect to see: "New websocket customer arrived!")

EDIT didn't realize examples/clientexample.nim was just connecting to https://websocket.org/echo.html ; but how about examples/serverexample.nim ? would be nice to have an example showing how to talk to the server created with examples/serverexample.nim

EDIT
works with:

  let ws = waitFor newAsyncWebsocketClient("localhost",
    Port 8080, "/?encoding=text", ssl = false, protocols = @["myfancyprotocol"])

would still make sense to include this in examples/clientexample.nim (say, controlled by a mode/flag to avoid code duplication)

Some observations

hey, I've been playing around with this library over the past couple of days and it's working very well so far. Awesome job :)

One thing I just painfully observed is that using send instead of sendText by accident can have some really confusing consequences. I just added a bunch of code to my program, both a "reading" procedure and a "writing" procedure. Suddenly my code just started to crash with a "socket closed". I was really confused and started to wonder whether there was a bug in asynchttpserver, asyncnet, or even in Nim itself. Turns out it was because I used send instead of sendText :\

So perhaps it would be a good idea to introduce a distinct type here to force the use of sendText? :)

Sample server crashes on socket close

websocket.nim version: 0.3.3
Nim version: 0.18.0, devel

  1. Create a server and client from the docs samples:

  2. Compile and run the server and client.

  3. Observe as they exchange data.

  4. Stop the client.

Expected behavior: the server continues running
Actual behavior: the server craches with this trace:

multicart_backend.nim(33) multicart_backend
asyncdispatch.nim(1654)  waitFor
asyncdispatch.nim(1514)  poll
asyncdispatch.nim(353)   runOnce
asyncdispatch.nim(189)   processPendingCallbacks
asyncmacro.nim(36)       recvFrame_continue
shared.nim(156)          recvFrameIter
[[reraised from:
multicart_backend.nim(33) multicart_backend
asyncdispatch.nim(1654)  waitFor
asyncdispatch.nim(1514)  poll
asyncdispatch.nim(353)   runOnce
asyncdispatch.nim(189)   processPendingCallbacks
asyncmacro.nim(36)       readData_continue
shared.nim(243)          readDataIter
asyncfutures.nim(302)    read
]]
[[reraised from:
multicart_backend.nim(33) multicart_backend
asyncdispatch.nim(1654)  waitFor
asyncdispatch.nim(1514)  poll
asyncdispatch.nim(353)   runOnce
asyncdispatch.nim(189)   processPendingCallbacks
asyncmacro.nim(36)       cb_continue
multicart_backend.nim(16) cbIter
asyncfutures.nim(302)    read
]]
[[reraised from:
multicart_backend.nim(33) multicart_backend
asyncdispatch.nim(1654)  waitFor
asyncdispatch.nim(1514)  poll
asyncdispatch.nim(353)   runOnce
asyncdispatch.nim(189)   processPendingCallbacks
asyncmacro.nim(36)       processRequest_continue
asynchttpserver.nim(259) processRequestIter
asyncfutures.nim(302)    read
]]
[[reraised from:
multicart_backend.nim(33) multicart_backend
asyncdispatch.nim(1654)  waitFor
asyncdispatch.nim(1514)  poll
asyncdispatch.nim(353)   runOnce
asyncdispatch.nim(189)   processPendingCallbacks
asyncmacro.nim(36)       processClient_continue
asynchttpserver.nim(288) processClientIter
asyncfutures.nim(302)    read
]]
[[reraised from:
multicart_backend.nim(33) multicart_backend
asyncdispatch.nim(1654)  waitFor
asyncdispatch.nim(1514)  poll
asyncdispatch.nim(353)   runOnce
asyncdispatch.nim(189)   processPendingCallbacks
asyncfutures.nim(348)    :anonymous
]]
Error: unhandled exception: socket closed
Async traceback:
  multicart_backend.nim(33) multicart_backend
  asyncdispatch.nim(1654)   waitFor
  asyncdispatch.nim(1514)   poll
    ## Processes asynchronous completion events
  asyncdispatch.nim(353)    runOnce
  asyncdispatch.nim(189)    processPendingCallbacks
    ## Executes pending callbacks
  asyncmacro.nim(36)        recvFrame_continue
    ## Resumes an async procedure
  shared.nim(156)           recvFrameIter
  #[
    multicart_backend.nim(33) multicart_backend
    asyncdispatch.nim(1654)   waitFor
    asyncdispatch.nim(1514)   poll
      ## Processes asynchronous completion events
    asyncdispatch.nim(353)    runOnce
    asyncdispatch.nim(189)    processPendingCallbacks
      ## Executes pending callbacks
    asyncmacro.nim(36)        readData_continue
      ## Resumes an async procedure
    shared.nim(243)           readDataIter
    asyncfutures.nim(302)     read
  ]#
  #[
    multicart_backend.nim(33) multicart_backend
    asyncdispatch.nim(1654)   waitFor
    asyncdispatch.nim(1514)   poll
      ## Processes asynchronous completion events
    asyncdispatch.nim(353)    runOnce
    asyncdispatch.nim(189)    processPendingCallbacks
      ## Executes pending callbacks
    asyncmacro.nim(36)        cb_continue
      ## Resumes an async procedure
    multicart_backend.nim(16) cbIter
    asyncfutures.nim(302)     read
  ]#
  #[
    multicart_backend.nim(33) multicart_backend
    asyncdispatch.nim(1654)   waitFor
    asyncdispatch.nim(1514)   poll
      ## Processes asynchronous completion events
    asyncdispatch.nim(353)    runOnce
    asyncdispatch.nim(189)    processPendingCallbacks
      ## Executes pending callbacks
    asyncmacro.nim(36)        processRequest_continue
      ## Resumes an async procedure
    asynchttpserver.nim(259)  processRequestIter
    asyncfutures.nim(302)     read
  ]#
  #[
    multicart_backend.nim(33) multicart_backend
    asyncdispatch.nim(1654)   waitFor
    asyncdispatch.nim(1514)   poll
      ## Processes asynchronous completion events
    asyncdispatch.nim(353)    runOnce
    asyncdispatch.nim(189)    processPendingCallbacks
      ## Executes pending callbacks
    asyncmacro.nim(36)        processClient_continue
      ## Resumes an async procedure
    asynchttpserver.nim(288)  processClientIter
    asyncfutures.nim(302)     read
  ]#
Exception message: socket closed
Exception type: [IOError]

Problems with gdax websocket

When trying to connect to GDAX's public websocket:

let ws = waitFor newAsyncWebsocket("wss://ws-feed.gdax.com:443/", 
                                   ctx=newContext(verifyMode=CVerifyNone))

I get Error: unhandled exception: server did not reply with a websocket upgrade: HTTP/1.1 520 Origin Error

Because I'm able to connect to wss://echo.websocket.org using the same function, and I'm able to connect using python (e.g. create_connection("wss://ws-feed.gdax.com:443/")), I suspect this is on websocket.nim's side. I've tried forking and fiddling around, but no dice

(I've tried it with both the production and sandbox feed)

edit: Forgot to post stats

  • OS: Ubuntu 16.04 (running in docker image nimlang/nim:latest)
  • websockets.nim: head

update: looking back at this, I see this is a CloudFlare specific error code:

While the 520 error can be triggered by very unique and strange edge-case scenarios, they are generally caused by:

Connection resets (following a successful TCP handshake)
Headers exceed Cloudflareโ€™s header size limit (over 8kb)
Empty response from origin
Invalid HTTP response
HTTP response missing response headers

But again, I was able to connect to it successfully in python and bash:

$ curl --include \
     --no-buffer \
     --header "Connection: Upgrade" \
     --header "Upgrade: websocket" \
     --header "Host: ws-feed.gdax.com:443" \
     --header "Origin: http://example.com:80" \
     --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
     --header "Sec-WebSocket-Version: 13" \
     https://ws-feed.gdax.com:443/

HTTP/2 200
date: Tue, 30 Jan 2018 08:00:27 GMT
content-type: text/plain
...

websocket/shared.nim(265, 5) Error: invalid else, all cases are already covered

bug triggered from another libary (/cc @brentp ) but pointing to here

using nim devel; it now disallows duplicate coverage in case statements

nim --version
Nim Compiler Version 0.19.9 [MacOSX: amd64]
Compiled at 2019-01-25
Copyright (c) 2006-2018 by Andreas Rumpf

git hash: 44923a1b1f158732b13fb938c94c0b02a2463e6d
active boot switches: -d:release
git clone https://github.com/brentp/nim-plotly
cd nim-plotly
nimble test
   Warning: Using env var NIM_LIB_PREFIX: /Users/timothee/git_clone//nim//Nim/
  Executing task test in /Users/timothee/git_clone/nim/nim-plotly/plotly.nimble
Hint: used config file '/Users/timothee/git_clone/nim/Nim/config/nim.cfg' [Conf]
Hint: used config file '/Users/timothee/.config/nim/nim.cfg' [Conf]
Hint: used config file '/Users/timothee/git_clone/nim/nim-plotly/examples/nim.cfg' [Conf]
Hint: used config file '/Users/timothee/git_clone/nim/Nim/config/config.nims' [Conf]
/Users/timothee/git_clone/nim/nim-plotly/examples/all.nim(8, 1) template/generic instantiation from here
/Users/timothee/git_clone/nim/nim-plotly/examples/fig9_heatmap.nim(6, 1) template/generic instantiation from here
/Users/timothee/git_clone/nim/nim-plotly/examples/fig9_heatmap.nim(16, 18) Warning: random is deprecated [Deprecated]
Hint:  [Link]
Hint: operation successful (66280 lines compiled; 1.143 sec total; 91.938MiB peakmem; Debug Build) [SuccessX]
Hint: /Users/timothee/git_clone/nim/nim-plotly/examples/all  [Exec]
/tmp/x.html
/tmp/x.html
/tmp/x.html
/tmp/x.html
/tmp/x.html
Hint: used config file '/Users/timothee/git_clone/nim/Nim/config/nim.cfg' [Conf]
Hint: used config file '/Users/timothee/.config/nim/nim.cfg' [Conf]
Hint: used config file '/Users/timothee/git_clone/nim/nim-plotly/examples/nim.cfg' [Conf]
Hint: used config file '/Users/timothee/git_clone/nim/Nim/config/config.nims' [Conf]
/Users/timothee/git_clone/nim/nim-plotly/examples/fig12_save_figure.nim(1, 1) template/generic instantiation from here
/Users/timothee/git_clone/nim/nim-plotly/src/plotly.nim(15, 1) template/generic instantiation from here
/Users/timothee/git_clone/nim/nim-plotly/src/plotly/image_retrieve.nim(1, 1) template/generic instantiation from here
/Users/timothee/.nimble/pkgs/websocket-0.3.4/websocket/shared.nim(221, 51) template/generic instantiation of `async` from here
/Users/timothee/.nimble/pkgs/websocket-0.3.4/websocket/shared.nim(265, 5) Error: invalid else, all cases are already covered
stack trace: (most recent call last)
/Users/timothee/git_clone/nim/nim-plotly/plotly.nimble(18, 8) testTask
/Users/timothee/git_clone/nim/Nim/lib/system/nimscript.nim(241, 7) exec
/Users/timothee/git_clone/nim/Nim/lib/system/nimscript.nim(241, 7) Error: unhandled exception: FAILED: nim c --lineDir:on --debuginfo --threads:on -r examples/fig12_save_figure.nim

Browser refreshing causes websocket server exception

websocket-0.3.5 latest version
Everytime enters the index.html again causes

[Warning] WS negotiation failed: the only supported sec-websocket-version is 13
pmserver.nim(157)        pmserver
pmserver.nim(141)        serve
asyncdispatch.nim(1656)  waitFor
asyncdispatch.nim(1516)  poll
asyncdispatch.nim(1282)  runOnce
asyncdispatch.nim(191)   processPendingCallbacks
asyncmacro.nim(36)       processRequest_continue
asynchttpserver.nim(275) processRequestIter
asyncnet.nim(628)        close
asyncdispatch.nim(1122)  closeSocket
ioselectors_epoll.nim(180) unregister
system.nim(3877)         failedAssertImpl
system.nim(3870)         raiseAssert
system.nim(2916)         sysFatal
[[reraised from:
pmserver.nim(157)        pmserver
pmserver.nim(141)        serve
asyncdispatch.nim(1656)  waitFor
asyncdispatch.nim(1516)  poll
asyncdispatch.nim(1282)  runOnce
asyncdispatch.nim(191)   processPendingCallbacks
asyncmacro.nim(36)       processClient_continue
asynchttpserver.nim(288) processClientIter
asyncfutures.nim(302)    read
]]
[[reraised from:
pmserver.nim(157)        pmserver
pmserver.nim(141)        serve
asyncdispatch.nim(1656)  waitFor
asyncdispatch.nim(1516)  poll
asyncdispatch.nim(1282)  runOnce
asyncdispatch.nim(191)   processPendingCallbacks
asyncfutures.nim(349)    :anonymous
]]
Error: unhandled exception: /usr/local/Nim/lib/pure/ioselects/ioselectors_epoll.nim(180, 11) `
not (pkey.ident == InvalidIdent)` Descriptor 15 is not registered in the selector!
Async traceback:
  pmserver.nim(157)          pmserver
  pmserver.nim(141)          serve
  asyncdispatch.nim(1656)    waitFor
  asyncdispatch.nim(1516)    poll
    ## Processes asynchronous completion events
  asyncdispatch.nim(1282)    runOnce
  asyncdispatch.nim(191)     processPendingCallbacks
    ## Executes pending callbacks
  asyncmacro.nim(36)         processRequest_continue
    ## Resumes an async procedure
  asynchttpserver.nim(275)   processRequestIter
  asyncnet.nim(628)          close
  asyncdispatch.nim(1122)    closeSocket
  ioselectors_epoll.nim(180) unregister
  system.nim(3877)           failedAssertImpl
  system.nim(3870)           raiseAssert
  system.nim(2916)           sysFatal
  #[
    pmserver.nim(157)          pmserver
    pmserver.nim(141)          serve
    asyncdispatch.nim(1656)    waitFor
    asyncdispatch.nim(1516)    poll
      ## Processes asynchronous completion events
    asyncdispatch.nim(1282)    runOnce
    asyncdispatch.nim(191)     processPendingCallbacks
      ## Executes pending callbacks
    asyncmacro.nim(36)         processClient_continue
      ## Resumes an async procedure
    asynchttpserver.nim(288)   processClientIter
    asyncfutures.nim(302)      read
  ]#
Exception message: /usr/local/Nim/lib/pure/ioselects/ioselectors_epoll.nim(180, 11) `
not (pkey.ident == InvalidIdent)` Descriptor 15 is not registered in the selector!
Exception type: [AssertionError]

as a websocket opened on page loading and closed on leaving.
our websocket server code is as follows: http://ix.io/1yUB

Clarification

What is "A robust RNG for the client data masking?"

Sending data over 0x7fff bytes breaks.

The problem is on
https://github.com/niv/websocket.nim/blob/master/websocket/shared.nim#L57
ret.write(int64 f.data.len.int32.htonl) and 0x7fff

Is not correct way to do, it should be some thing like:

if f.data.len > 125 and f.data.len <= 0xffff:
    ret.write(uint16 f.data.len.uint16.htons)
elif f.data.len > 0xffff:
    var len = f.data.len
    ret.write char((len shr 56) and 255)
    ret.write char((len shr 48) and 255)
    ret.write char((len shr 40) and 255)
    ret.write char((len shr 32) and 255)
    ret.write char((len shr 24) and 255)
    ret.write char((len shr 16) and 255)
    ret.write char((len shr 8) and 255)
    ret.write char(len and 255)

Websocket client gets unhandled IndexError exception

Nim Compiler Version 0.19.2 [MacOSX: amd64], Compiled at 2018-12-31
websocket [0.3.5]

If you run the following websocket client program you'll see that for any sleep time argument longer than 54000 (millis), it will get an IndexError at approx 54 seconds into the run.

Command Line Result
time ./buggy_websocket 10000 As expected, runs for ~10 seconds
time ./buggy_websocket 20000 As expected, runs for ~20 seconds
time ./buggy_websocket 120000 Unexpectedly runs for only ~54 seconds
time ./buggy_websocket 240000 Unexpectedly runs for only ~54 seconds

Some thoughts:

  • Is there a timeout or some setting that I'm missing that's causing the error at 54 seconds?
  • For the times that the websocket is closed as expected, the library still throws an IndexError on websocket.close(). Is there a better way to close the socket without getting the error?

import asyncnet, asyncdispatch, os, websocket
from strutils import parseInt
from net import newContext, SSLContext, protSSLv23, CVerifyNone

let
  WS_HOST = "paper-api.alpaca.markets"
  WS_PATH = "/stream"
  WS_PORT = Port(443)
  WS_SSL = true

proc reader(ws: AsyncWebSocket) {.async.} =
  while true:
    let (opcode, data) = await ws.readData()
    echo "read -- opcode: ", $opcode, "  data: ", $data

proc pinger(ws: AsyncWebSocket) {.async.} =
  while true:
    await ws.sendPing()
    await sleepAsync(20_000)

when isMainModule:
  let sleepTimeMillis = parseInt(paramStr(1))
  echo "Sleep time is " & $sleepTimeMillis

  try:
    let sslCtx = newContext(protSSLv23, verifyMode = CVerifyNone)
    doAssert(not sslCtx.isNil, "Failed to initialize SSL context")
    let ws = waitFor newAsyncWebsocketClient(WS_HOST, WS_PORT,
                                             WS_PATH, WS_SSL,
                                             ctx = sslCtx)
    echo "Connected!"

    asyncCheck reader(ws)
    asyncCheck pinger(ws)

    waitFor sleepAsync(sleepTimeMillis)
    waitFor ws.close()

  except ProtocolError:
    echo ".... connection failed!"
    echo getCurrentExceptionMsg()
  except OSError:
    echo ".... connection failed!"
    echo getCurrentExceptionMsg()

Example stack trace

Sleep time is 240000                                                            
Connected!                                                                      
read -- opcode: Cont  data: 5c882fb62f85f47000000001                            
read -- opcode: Cont  data: 5c882fca2f85f47000000002                            
read -- opcode: Cont  data: 5c882fde2f85f47000000003                            
buggy_websocket.nim(36)  buggy_websocket                                        
asyncdispatch.nim(1656)  waitFor                                                
asyncdispatch.nim(1516)  poll                                                   
asyncdispatch.nim(1282)  runOnce                                                
asyncdispatch.nim(191)   processPendingCallbacks                                
asyncmacro.nim(36)       readData_continue                                      
shared.nim(136)          readDataIter
shared.nim(139)          makeFrame
shared.nim(134)          makeFrame
system.nim(2916)         sysFatal
[[reraised from:
buggy_websocket.nim(36)  buggy_websocket
asyncdispatch.nim(1656)  waitFor
asyncdispatch.nim(1516)  poll
asyncdispatch.nim(1282)  runOnce
asyncdispatch.nim(191)   processPendingCallbacks                               
asyncmacro.nim(36)       reader_continue
buggy_websocket.nim(13)  readerIter
asyncfutures.nim(302)    read
]]
[[reraised from:
buggy_websocket.nim(36)  buggy_websocket
asyncdispatch.nim(1656)  waitFor
asyncdispatch.nim(1516)  poll
asyncdispatch.nim(1282)  runOnce
asyncdispatch.nim(191)   processPendingCallbacks                               
asyncfutures.nim(349)    :anonymous
]]
Error: unhandled exception: index out of bounds                                
Async traceback:
  buggy_websocket.nim(36) buggy_websocket                                      
  asyncdispatch.nim(1656) waitFor
  asyncdispatch.nim(1516) poll
    ## Processes asynchronous completion events                                
  asyncdispatch.nim(1282) runOnce
  asyncdispatch.nim(191)  processPendingCallbacks                              
    ## Executes pending callbacks
  asyncmacro.nim(36)      readData_continue                                    
    ## Resumes an async procedure
  shared.nim(136)         readDataIter
  shared.nim(139)         makeFrame
  shared.nim(134)         makeFrame
  system.nim(2916)        sysFatal
  #[
    buggy_websocket.nim(36) buggy_websocket                                    
    asyncdispatch.nim(1656) waitFor
    asyncdispatch.nim(1516) poll
      ## Processes asynchronous completion events
    asyncdispatch.nim(1282) runOnce
    asyncdispatch.nim(191)  processPendingCallbacks
      ## Executes pending callbacks
    asyncmacro.nim(36)      reader_continue
      ## Resumes an async procedure
    buggy_websocket.nim(13) readerIter
    asyncfutures.nim(302)   read
  ]#
Exception message: index out of bounds
Exception type: [IndexError]

real    0m54.495s
user    0m0.029s
sys     0m0.013s

Example client doesn't compile

The example at http://niv.github.io/websocket.nim/docs/0.1.1/websocket/client.html is missing a very important first line import websocket/client. After adding this line, Nim 0.18.0 still won't compile it:

/Users/gradha/.nimble/pkgs/websocket-0.2.1/websocket/shared.nim(87, 49) template/generic instantiation from here
/Users/gradha/.nimble/pkgs/websocket-0.2.1/websocket/shared.nim(130, 14) Error: type mismatch: got <byte> but expected 'int'

I see the git repository is at 0.2.3, so I tried with #head but then there is a missing symbol:

/Users/gradha/.nimble/pkgs/websocket-#head/websocket/client.nim(47, 30) template/generic instantiation from here
/Users/gradha/.nimble/pkgs/websocket-#head/websocket/client.nim(46, 23) Error: undeclared identifier: 'newContext'

Secure websockets?

Any plan on adding support for secure websockets , wss using TLS or whatever :)

Add support for close codes and messages

Close opcodes can have data after them, the first 2 bytes being unsigned close code and the rest being optional error message. Currently whenever the library sees the close opcode it closes instantly instead of waiting for the remaining data

type mismatch: got <userAgent: string, sslContext: SslContext>

Running the client example:

Nim Compiler Version 1.0.99 [Windows: amd64]
Compiled at 2019-10-18
Copyright (c) 2006-2019 by Andreas Rumpf

git hash: 832b0a0232e610c1935aaf6ce0b45f69199f8a19
active boot switches: -d:release

C:\Users\jairo\.nimble\pkgs\websocket-0.4.0\websocket\client.nim(106, 34) Error: type mismatch: got <userAgent: string, sslContext: SslContext>
but expected one of:
proc newAsyncHttpClient(userAgent = defUserAgent; maxRedirects = 5;
                       sslContext = getDefaultSSL(); proxy: Proxy = nil): AsyncHttpClient
  first type mismatch at position: 2
  required type for sslContext: SSLContext
  but expression 'sslContext = sslContext' is of type: SslContext

expression: newAsyncHttpClient(userAgent = userAgent, sslContext = sslContext)

ws.readData() unable to handle a null result

When I receive data from the websocket that has a null result, the program crashes.

In the attached test case, if you comment out the line that sends the subscribe request the program doesn't crash. This is because the message that is sent back includes a null result. However the websocket library should be able to handle this.

Here's the output from a Chrome websocket extension that does the same thing. The result from the subscribe request is null, but the websocket loop continues to function as expected.

{"e":"kline","E":1660633181407,"s":"BNBUSDT","k":{"t":1660633140000,"T":1660633199999,"s":"BNBUSDT","i":"1m","f":578704188,"L":578704249,"o":"317.00000000","c":"316.90000000","h":"317.10000000","l":"316.80000000","v":"111.62500000","n":62,"x":false,"q":"35378.63340000","V":"12.78200000","Q":"4052.67600000","B":"0"}}
{"e":"kline","E":1660633188428,"s":"BNBUSDT","k":{"t":1660633140000,"T":1660633199999,"s":"BNBUSDT","i":"1m","f":578704188,"L":578704250,"o":"317.00000000","c":"316.90000000","h":"317.10000000","l":"316.80000000","v":"111.65900000","n":63,"x":false,"q":"35389.40800000","V":"12.81600000","Q":"4063.45060000","B":"0"}}
{  "method": "SUBSCRIBE",  "params": [    "adausdt@kline_1m"  ],  "id": 1 }
{"result":null,"id":1}
{"e":"kline","E":1660633189014,"s":"ADAUSDT","k":{"t":1660633140000,"T":1660633199999,"s":"ADAUSDT","i":"1m","f":403020801,"L":403020878,"o":"0.55370000","c":"0.55320000","h":"0.55370000","l":"0.55310000","v":"39277.50000000","n":78,"x":false,"q":"21734.35789000","V":"14835.80000000","Q":"8208.74871000","B":"0"}}
{"e":"kline","E":1660633190748,"s":"BNBUSDT","k":{"t":1660633140000,"T":1660633199999,"s":"BNBUSDT","i":"1m","f":578704188,"L":578704251,"o":"317.00000000","c":"316.80000000","h":"317.10000000","l":"316.80000000","v":"111.75900000","n":64,"x":false,"q":"35421.08800000","V":"12.81600000","Q":"4063.45060000","B":"0"}}

test_ws.zip

help sendText async is recieved to html webview binary

hello, at the beginning I was very happy because while reading the code I found that the protocol was respected.

But when I used this with webview or the firefox browser the sendText sent by the server is binary I work in async

could you make it text, because the web protocol receives it like a blod, of course we can set up a gas factory to read but that loses its charm and makes conversations very complex

your example with await ws.sendText ("thanks for the data!") sends a binary received as a blod

thank you (excuse me for my english it's google)

value out of range: -1

This error can appear anywhere between a few hours to a few days, but it's always the same and I can't find the cause for it.

value out of range: -1
  recv's lead up to read of failed Future:
    Traceback (most recent call last)
    main.nim(93)             main
    asyncdispatch.nim(271)   waitFor
    asyncdispatch.nim(298)   poll
    asyncdispatch.nim(426)   :anonymous
    asyncfutures.nim(109)    complete
    asyncmacro.nim(34)       cb
    asyncmacro.nim(334)      appeaseSslIter
    asyncfutures.nim(109)    complete
    asyncmacro.nim(34)       cb
    asyncnet.nim(388)        recvIter
    system.nim(2662)         sysFatal
  recvFrame's lead up to read of failed Future:
    Traceback (most recent call last)
    main.nim(93)             main
    asyncdispatch.nim(271)   waitFor
    asyncdispatch.nim(298)   poll
    asyncdispatch.nim(426)   :anonymous
    asyncfutures.nim(109)    complete
    asyncmacro.nim(34)       cb
    asyncmacro.nim(334)      appeaseSslIter
    asyncfutures.nim(109)    complete
    asyncmacro.nim(34)       cb
    asyncnet.nim(388)        recvIter
    system.nim(2662)         sysFatal
    [[reraised from:
    main.nim(93)             main
    asyncdispatch.nim(271)   waitFor
    asyncdispatch.nim(298)   poll
    asyncdispatch.nim(426)   :anonymous
    asyncfutures.nim(109)    complete
    asyncmacro.nim(34)       cb
    asyncmacro.nim(334)      appeaseSslIter
    asyncfutures.nim(109)    complete
    asyncmacro.nim(50)       cb
    asyncfutures.nim(156)    fail
    asyncmacro.nim(34)       cb
    asyncmacro.nim           recvFrameIter
    asyncfutures.nim(220)    read
    ]]

Does closing websocket with a specific code work?

I've been making my own Discord library in Nim, I've been wondering if it is possible to close a websocket with a specific close code? I am aware that you can do that, but when I close the code, it doesn't respond with a close code provided in the procedure.

I want to know if there is a working code or solution to that or not, because I think it is handy.

If not, then the behaviour should be like this:

Close the socket manually (code: 4000 for example), when some error occurs when websocket is closed, find the close data by extractCloseData(), then the extracted code should be the one provided in the close proc (which is 4000 as an example.).

the sample client doesn't compile

import websocket, asyncnet, asyncdispatch

let ws = waitFor newAsyncWebsocketClient("localhost", Port(8080),
  path = "/", protocols = @["myfancyprotocol"])
echo "connected!"

proc ping() {.async.} =
  while true:
    await sleepAsync(6000)
    echo "ping"
    await ws.sendPing()

proc read() {.async.} =
  while true:
    let (opcode, data) = await ws.readData()
    echo "(opcode: ", opcode, ", data: ", data, ")"

asyncCheck read()
asyncCheck ping()
runForever()

with the following error:

.nimble/pkgs/websocket-0.4.0/websocket/client.nim(106, 34) Error: type mismatch: got <userAgent: string, sslContext: SslContext>
but expected one of:
proc newAsyncHttpClient(userAgent = defUserAgent; maxRedirects = 5;
                       sslContext = getDefaultSSL(); proxy: Proxy = nil): AsyncHttpClient
  first type mismatch at position: 2
  required type for sslContext: SSLContext
  but expression 'sslContext = sslContext' is of type: SslContext

Client doesn't keep receiving data depending on how it is created

I have written a very simple program which subscribe to BitMEX exchange trade data.

import websocket, asyncnet, asyncdispatch

# This doesn't work (method A); only initial welcome message is received
let ws = waitFor newAsyncWebsocketClient("wss://www.bitmex.com:443/realtime?subscribe=trade:XBTUSD")

# This works (method B).
# let ws = waitFor newAsyncWebsocketClient("www.bitmex.com", Port 443, "/realtime?subscribe=trade:XBTUSD", ssl=true)

proc reader() {.async.} =
    while true:
        let read = await ws.readData()
        echo "read: ", read

asyncCheck reader()
runForever()

When the client object is created by providing a full URI like method A above, only the initial welcome message is received.

read: (opcode: Text, data: "{\"info\":\"Welcome to the BitMEX Realtime API.\",\"version\":\"2018-09-07T19:37:21.000Z\",\"timestamp\":\"2018-09-12T16:52:07.992Z\",\"docs\":\"https://www.bitmex.com/app/wsAPI\",\"limit\":{\"remaining\":38}}")

However, if it is created like method B by providing host name, port and the path separately, it does receive subsequent trade data successfully.

read: (opcode: Text, data: "{\"info\":\"Welcome to the BitMEX Realtime API.\",\"version\":\"2018-09-07T19:37:21.000Z\",\"timestamp\":\"2018-09-12T17:03:15.805Z\",\"docs\":\"https://www.bitmex.com/app/wsAPI\",\"limit\":{\"remaining\":39}}")
read: (opcode: Text, data: "{\"success\":true,\"subscribe\":\"trade:XBTUSD\",\"request\":{\"op\":\"subscribe\",\"args\":\"trade:XBTUSD\"}}")
read: (opcode: Text, data: "{\"table\":\"trade\",\"action\":\"partial\",\"keys\":[],\"types\":{\"timestamp\":\"timestamp\",\"symbol\":\"symbol\",\"side\":\"symbol\",\"size\":\"long\",\"price\":\"float\",\"tickDirection\":\"symbol\",\"trdMatchID\":\"guid\",\"grossValue\":\"long\",\"homeNotional\":\"float\",\"foreignNotional\":\"float\"},\"foreignKeys\":{\"symbol\":\"instrument\",\"side\":\"side\"},\"attributes\":{\"timestamp\":\"sorted\",\"symbol\":\"grouped\"},\"filter\":{\"symbol\":\"XBTUSD\"},\"data\":[{\"timestamp\":\"2018-09-12T17:03:13.014Z\",\"symbol\":\"XBTUSD\",\"side\":\"Buy\",\"size\":200,\"price\":6260.5,\"tickDirection\":\"ZeroPlusTick\",\"trdMatchID\":\"0d0a4c64-17e3-4738-f243-b33dffa52a1e\",\"grossValue\":3194600,\"homeNotional\":0.031946,\"foreignNotional\":200}]}")
r

What's going wrong with the client when created like method A?

Opening server example in regular browser results in crash

I'm running the websocket server example but opening the address in a web browser (ie regular http, not ws) crashes the program. Reading the source code I would expect it to return a "Websocket negotiation failed: ... " 400 http error but it doesn't.

This is the error message I get:

    dash.nim(36)             dash
    dash.nim(32)             main
    webserver.nim(35)        webServer
    asyncdispatch.nim(1654)  waitFor
    asyncdispatch.nim(1514)  poll
      ## Processes asynchronous completion events
    asyncdispatch.nim(1280)  runOnce
    asyncdispatch.nim(189)   processPendingCallbacks
      ## Executes pending callbacks
    asyncmacro.nim(39)       processRequest_continue
      ## Resumes an async procedure
    asynchttpserver.nim(259) processRequestIter
    asyncmacro.nim(307)      cb
    asyncmacro.nim(39)       cb_continue
      ## Resumes an async procedure
    webserver.nim(8)         cbIter
    asyncfutures.nim(302)    read
  ]#
  #[
    dash.nim(36)             dash
    dash.nim(32)             main
    webserver.nim(35)        webServer
    asyncdispatch.nim(1654)  waitFor
    asyncdispatch.nim(1514)  poll
      ## Processes asynchronous completion events
    asyncdispatch.nim(1280)  runOnce
    asyncdispatch.nim(189)   processPendingCallbacks
      ## Executes pending callbacks
    asyncmacro.nim(39)       processRequest_continue
      ## Resumes an async procedure
    asynchttpserver.nim(259) processRequestIter
    asyncfutures.nim(302)    read
  ]#
  #[
    dash.nim(36)             dash
    dash.nim(32)             main
    webserver.nim(35)        webServer
    asyncdispatch.nim(1654)  waitFor
    asyncdispatch.nim(1514)  poll
      ## Processes asynchronous completion events
    asyncdispatch.nim(1280)  runOnce
    asyncdispatch.nim(189)   processPendingCallbacks
      ## Executes pending callbacks
    asyncmacro.nim(36)       processClient_continue
      ## Resumes an async procedure
    asynchttpserver.nim(288) processClientIter
    asyncfutures.nim(302)    read
  ]#
Exception message: Async procedure (verifyWebsocketRequest) yielded `nil`, are you await'ing a `nil` Future?
Exception type: [AssertionError]

Building with "nim c -d:ssl --threads:on dash.nim" where "dash.nim" simply executes the code from the example server.

Nim Compiler Version 0.19.0 [MacOSX: amd64]

SSL3_GET_RECORD:wrong version number on newAsyncWebsocket

On Windows 10 x64

Code:

let ws = waitFor newAsyncWebSocket("gateway.discord.gg", Port 80, "?encoding=json&v=6", ssl = true)

Error (ws.nim(10) is the code above):

Traceback (most recent call last)
ws.nim(10)               ws
asyncdispatch.nim(271)   waitFor
asyncdispatch.nim(326)   poll
asyncdispatch.nim(180)   processPendingCallbacks
asyncdispatch.nim(1271)  :anonymous
asyncfutures.nim(118)    complete
asyncmacro.nim(34)       cb
asyncmacro.nim(334)      sendPendingSslDataIter
asyncfutures.nim(118)    complete
asyncmacro.nim(34)       cb
asyncnet.nim(237)        connectIter
net.nim(421)             getSslError
net.nim(431)             raiseSSLError
[[reraised from:
ws.nim(10)               ws
asyncdispatch.nim(271)   waitFor
asyncdispatch.nim(326)   poll
asyncdispatch.nim(180)   processPendingCallbacks
asyncdispatch.nim(1271)  :anonymous
asyncfutures.nim(118)    complete
asyncmacro.nim(34)       cb
asyncmacro.nim(334)      sendPendingSslDataIter
asyncfutures.nim(118)    complete
asyncmacro.nim(50)       cb
asyncfutures.nim(156)    fail
asyncmacro.nim(34)       cb
asyncmacro.nim           newAsyncWebsocketIter
asyncfutures.nim(220)    read
]]
[[reraised from:
ws.nim(10)               ws
asyncdispatch.nim(1345)  waitFor
asyncfutures.nim(220)    read
]]
Error: unhandled exception: error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
  connect's lead up to read of failed Future:
    Traceback (most recent call last)
    ws.nim(10)               ws
    asyncdispatch.nim(271)   waitFor
    asyncdispatch.nim(326)   poll
    asyncdispatch.nim(180)   processPendingCallbacks
    asyncdispatch.nim(1271)  :anonymous
    asyncfutures.nim(118)    complete
    asyncmacro.nim(34)       cb
    asyncmacro.nim(334)      sendPendingSslDataIter
    asyncfutures.nim(118)    complete
    asyncmacro.nim(34)       cb
    asyncnet.nim(237)        connectIter
    net.nim(421)             getSslError
    net.nim(431)             raiseSSLError
  newAsyncWebsocket's lead up to read of failed Future:
    Traceback (most recent call last)
    ws.nim(10)               ws
    asyncdispatch.nim(271)   waitFor
    asyncdispatch.nim(326)   poll
    asyncdispatch.nim(180)   processPendingCallbacks
    asyncdispatch.nim(1271)  :anonymous
    asyncfutures.nim(118)    complete
    asyncmacro.nim(34)       cb
    asyncmacro.nim(334)      sendPendingSslDataIter
    asyncfutures.nim(118)    complete
    asyncmacro.nim(34)       cb
    asyncnet.nim(237)        connectIter
    net.nim(421)             getSslError
    net.nim(431)             raiseSSLError
    [[reraised from:
    ws.nim(10)               ws
    asyncdispatch.nim(271)   waitFor
    asyncdispatch.nim(326)   poll
    asyncdispatch.nim(180)   processPendingCallbacks
    asyncdispatch.nim(1271)  :anonymous
    asyncfutures.nim(118)    complete
    asyncmacro.nim(34)       cb
    asyncmacro.nim(334)      sendPendingSslDataIter
    asyncfutures.nim(118)    complete
    asyncmacro.nim(50)       cb
    asyncfutures.nim(156)    fail
    asyncmacro.nim(34)       cb
    asyncmacro.nim           newAsyncWebsocketIter
    asyncfutures.nim(220)    read
    ]] [SslError]

Tried changing the SSL context version to SSLv23 and TLSv1 yet still the same error.

Discord doesn't allow ssl = false and gives a 400 Bad Request on websocket upgrade.

Can't connect to secure websocket

This is via the wss:// protocol.

The error is:

Async traceback:
  C:\Users\jason\.choosenim\toolchains\nim-1.6.6\lib\pure\asyncdispatch.nim(1961)               waitFor
  C:\Users\jason\.choosenim\toolchains\nim-1.6.6\lib\pure\asyncdispatch.nim(1653)               poll
  C:\Users\jason\.choosenim\toolchains\nim-1.6.6\lib\pure\asyncdispatch.nim(419)                runOnce
  C:\Users\jason\.choosenim\toolchains\nim-1.6.6\lib\pure\asyncdispatch.nim(234)                processPendingCallbacks
  C:\Users\jason\.choosenim\toolchains\nim-1.6.6\lib\pure\asyncmacro.nim(28)                    sendNimAsyncContinue
  C:\Users\jason\.choosenim\toolchains\nim-1.6.6\lib\pure\asyncnet.nim(271)                     sendIter
  C:\Users\jason\.choosenim\toolchains\nim-1.6.6\lib\pure\asyncnet.nim(221)                     getSslError
  C:\Users\jason\.choosenim\toolchains\nim-1.6.6\lib\pure\net.nim(548)                          raiseSSLError
..
Exception message: error:141A90B5:SSL routines:ssl_cipher_list_to_bytes:no ciphers available

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.