Closed gfwilliams closed 8 years ago
Possibly add normal sockets first (a la nodejs.org/api/net.html#net_net) and then a module on top for websockets?
Would the socket module that's been implemented support ws? Or better yet, how close is the stream implementation to node?
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.
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.
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.
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.
Awesome! Might it make sense to use the WebSocket API that exists in the browser?
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
@samehhady By ws
, are you specifically referring to the npm module ws?
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>
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.
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
I will update the WebSocket Espruino page with detailed info on how to get started from Espruino side & Node.js side
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.
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?
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.
great, thx for pointing this out, I will update it soon
Closing this, as thanks to all your work we now have working WebSockets - both client and server: http://www.espruino.com/ws
@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
.
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.
@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 ;).
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
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!!
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.
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?
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?
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.
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 }
Ahh, interesting - please can you try ESP32.enableBLE(false)
(which I think will cause a reboot) and see if that makes any difference?
Base on this: https://github.com/einaros/ws