Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSockets #258

Closed
gfwilliams opened this issue Mar 20, 2014 · 29 comments
Closed

WebSockets #258

gfwilliams opened this issue Mar 20, 2014 · 29 comments

Comments

@gfwilliams
Copy link
Member

Base on this: https://github.com/einaros/ws

@gfwilliams
Copy link
Member Author

Possibly add normal sockets first (a la nodejs.org/api/net.html#net_net) and then a module on top for websockets?

@RangerMauve
Copy link

Would the socket module that's been implemented support ws? Or better yet, how close is the stream implementation to node?

@gfwilliams
Copy link
Member Author

The stream implementation is designed to be pretty close to Node - if something doesn't work I'd be interested to see if it could be fixed.

It should be possible to implement websockets using the existing sockets implementation - the only issue as far as I can see is the hash function that's needed for setup, but that shouldn't be too bad.

Am I right in thinking that a websocket connection can be made to a normal HTTP server that supports it? I guess that would be the main use-case for this? If so it would probably require some support inside Espruino itself, but since I've added the code to split out HTTP and socket functionality, adding Websocket support shouldn't be too bad.

@RangerMauve
Copy link

Yeah, you just start a regular HTTP request-response cycle, and add some extra headers. The hashing function is just SHA-1, so it probably wouldn't be too hard to add in. Might make sense to just re-use an existing js implementation like this from the get go and do it all in JS-land. I haven't ordered my espruino yet so I can't really experiment much, but once I do I'll probably play around with it.

@gfwilliams
Copy link
Member Author

Just to add to this, you could test for Websockets in the server like this:

--- a/libs/network/socketserver.c
+++ b/libs/network/socketserver.c
@@ -300,22 +300,6 @@ bool socketServerConnectionsIdle(JsNetwork *net) {
             if (!hadHeaders && httpParseHeaders(&receiveData, connection, true)) {
               hadHeaders = true;
               jsvObjectSetChildAndUnLock(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders));
-
-              JsVar *headers = jsvObjectGetChild(connection, "headers", 0);
-              if (headers) {
-                JsVar *wsKey = 0;
-                if (jsvIsStringEqualAndUnLock(jsvObjectGetChild(headers, "Connection", 0), "Upgrade") &&
-                    jsvIsStringEqualAndUnLock(jsvObjectGetChild(headers, "Upgrade", 0), "websocket") &&
-                    (wsKey = jsvObjectGetChild(headers, "Sec-WebSocket-Key",0))) {
-                  // https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake
-                  jsvAppendString(wsKey, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
-                  // SHA1 hash
-                  // base64_encode
-                  jsiConsolePrintf("Websockets baby!\n");
-                }
-                jsvUnLock2(headers, wsKey);
-              }

Once the key was figured out it's then a matter of sending a few headers back with serverResponseWriteHead, doing socketSetType(req, ST_WEBSOCKET); and then making sure that in the rest of the handlers, we encode/decode things properly if socketType==ST_WEBSOCKET.

At some point soon I'll get some more cryto added, and sticking a SHA1 hash in should be easier.

@samehhady
Copy link

I've managed to write a websocket module for Espruino, you can try it out here:
http://www.espruino.com/ws

Currently you can use it as a websocket client only.

@RangerMauve
Copy link

Awesome! Might it make sense to use the WebSocket API that exists in the browser?

@samehhady
Copy link

while it should be possible, i do prefer to use the new html5 ws client, it can be used on all modern browsers but still you will need a websocket server to connect both browser client and Espruino client to and establish a connection between them

@RangerMauve
Copy link

@samehhady By ws, are you specifically referring to the npm module ws?

@samehhady
Copy link

No, mainly we are using the nodejs npm ws module to start a websocket server, but for us to use websocket from the browser we can use the websocket api that is implemented now mostly on all modern browsers. here is an example on how to use it.

<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
    var wsUri = "ws://192.168.0.1:8080";
    var output;

    function init() {
        output = document.getElementById("output");
        testWebSocket();
    }

    function testWebSocket() {
        websocket = new WebSocket(wsUri);
        websocket.onopen = function(evt) {
            onOpen(evt)
        };
        websocket.onclose = function(evt) {
            onClose(evt)
        };
        websocket.onmessage = function(evt) {
            onMessage(evt)
        };
        websocket.onerror = function(evt) {
            onError(evt)
        };
    }

    function onOpen(evt) {
        writeToScreen("CONNECTED");
        doSend("WebSocket rocks");
    }

    function onClose(evt) {
        writeToScreen("DISCONNECTED");
    }

    function onMessage(evt) {
        writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data + '</span>');
        websocket.close();
    }

    function onError(evt) {
        writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
    }

    function doSend(message) {
        writeToScreen("SENT: " + message);
        websocket.send(message);
    }

    function writeToScreen(message) {
        var pre = document.createElement("p");
        pre.style.wordWrap = "break-word";
        pre.innerHTML = message;
        output.appendChild(pre);
    }
    window.addEventListener("load", init, false);
</script>
<h2>WebSocket Test</h2>
<div id="output"></div>

@RangerMauve
Copy link

I may be misunderstanding something. I was commenting on how the library you made, although it's really awesome, doesn't seem to be using the standard interface that the browser provides. In your response to that, did you mean that you prefer to use the ws module on npm? Or were you agreeing and just stating that you like the browser interface? :P Sorry.

@samehhady
Copy link

For the current WebSocket implementation on Espruino I am following the standards of the rfc6455 Websocket protocol. these are the standards for anything related to websockets.

https://tools.ietf.org/html/rfc6455

In the current implementation I am using the minimum requirements and thats why I am currently only accepting messages less than 127 characters. But for the process of handhaking, framing & using the opcode they are all implemented already.

I do recommend using the npm ws server actually its a must (https://www.npmjs.com/package/ws). BUT if you want lets say to control Espruino from the web browser in that case you need an extra step which is the one I provided in my previous post

@samehhady
Copy link

I will update the WebSocket Espruino page with detailed info on how to get started from Espruino side & Node.js side

@RangerMauve
Copy link

What I was trying to get at is that rather than using .connect() and .on() in your library, it might be better to use the same API as the one in the browser. If not that, then it'd make sense to at least use the same API as the ws module so that the two would be interchangeable.

@samehhady
Copy link

Fair enough, I guess in that case it should be same as the ws module which would be something like:

var WebSocket = require('ws');
var ws = new WebSocket('ws://192.168.0.10', {
  port:8080,
  protocolVersion: 13, 
  origin: 'Espruino'
});

ws.on('open', function open() {
  console.log('connected');
});

ws.on('close', function close() {
  console.log('disconnected');
});

ws.on('message', function message(data) {
  console.log("MSG: " + data);
}

In that case it will be exact the same as the node.js module, what you think?

@RangerMauve
Copy link

Yeah, that'd be the best. Especially because it means that modules on npm could potentially use it directly as though it was the actual ws module.

@samehhady
Copy link

great, thx for pointing this out, I will update it soon

@gfwilliams
Copy link
Member Author

Closing this, as thanks to all your work we now have working WebSockets - both client and server: http://www.espruino.com/ws

@daveamit
Copy link

daveamit commented Jun 9, 2017

@gfwilliams I greatly appreciate the work you guys have been doing! Hats off! I was wondering if http://www.espruino.com/ws supports secure web socket support wss, currently it works great with ws.

@gfwilliams
Copy link
Member Author

Thanks! Yes, it should be fine on devices like Espruino WiFi and Pico that support HTTPS connections - wss:// is only ws over HTTPS.

The only gotcha is memory usage - I think it might be a little tight at the moment (especially on the Pico) but I have some stuff in the works that'll make that better. Using the save on send and modules as functions should help you out massively for now though.

@daveamit
Copy link

daveamit commented Jun 9, 2017

@gfwilliams Thanks for quick response. I'm writing a module on Lolin NodeMCU V3 (ESP8266 12E), flashed it with espruino 1V92. I tried with wss but did not work, I guess its not supported ....... yet ;).

@gfwilliams
Copy link
Member Author

I'm afraid there's no HTTPS on Espruino for ESP8266 at the moment. To do it in a spec compliant way you need bags of RAM for the packet buffers. If you need HTTPS/wss, best bet is to use Espruino Wifi: http://www.espruino.com/WiFi

@recursivecodes
Copy link

Are there still plans for a new WiFi board? That page says "We will be releasing a new WiFi + Bluetooth board in 2022" and it's nearly 2024. It's impossible to find the old board for sale anymore, and with no replacement on deck it makes things difficult! I'd love to buy (several) official boards to support the project!!

@gfwilliams
Copy link
Member Author

gfwilliams commented Aug 3, 2023

Thanks! There is still a plan for one (initially a much-updated Pixl.js board with USB, SD card and WiFi) but I'm afraid realistically it won't arrive until 2024.

It's just me working full-time on this, and Bangle.js 2 has taken so much time to support it's got in the way of my plans for new devices. WiFi itself is a bit of a tricky one as Espruino's real selling point is how low-power it is, but as soon as you boot WiFi up that goes out the window. At that point, you're comparing an Espruino WiFi board with a whole raft of low-cost Linux boards that'll natively run Node.js and it doesn't look like such a great deal any more.

For now, if you just want WiFi I'd say take a look at ESP32. While they don't support Espruino they are cheap, and the ESP32 port of Espruino is doing pretty well at the moment.

@recursivecodes
Copy link

I totally get it, Gordon. I can appreciate the time it takes to design/manufacture and support this project. I've been using the ESP32 port on a WROOM-32 board, but the lack of support for HTTPS is killing my use case/demo. And the benefit of using JavaScript is a something I'd like to highlight in my demo. I have just ordered a few ESP32-S3-Devkit-Lipo boards from Olimex, I wonder if they'll be able to handle https? Otherwise, would there be any chance to do another small run of the discontinued board to sell in the meantime whilst you work on the new board?

@gfwilliams
Copy link
Member Author

Unfortunately one of the reasons for not doing another run was some of the components were discontinued, so I'd have had to have done a new revision of the Wifi board anyway...

However I am surprised about the lack of HTTPS in the ESP32 build - I just did a quick check and as far as I can tell it should be built in?

@recursivecodes
Copy link

I'll check again this evening, but I was getting errors with it that I believe were memory related. I'll let you know after I play around with it a bit more.

@recursivecodes
Copy link

Here's the problem I'm seeing on an ESP32:

Wifi: connected
Uncaught InternalError: Failed! mbedtls_ssl_setup: Not enough memory
 at line 51 col 4
  });
   ^
in function "test" called from line 54 col 6
test();
     ^
>process.memory()
={ free: 4706, usage: 451, total: 5157, history: 0,
  gc: 0, gctime: 4.596, blocksize: 14 }

@gfwilliams
Copy link
Member Author

Ahh, interesting - please can you try ESP32.enableBLE(false) (which I think will cause a reboot) and see if that makes any difference?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants