Streaming data with HTTP/1.1 Transfer-Encoding: Chunked

I've considered this before, but never really had a need for it.  In working on an AI training data project this (sending data in chunks) was touted to make a demo look "better" - data is available in chunks and this can used to make it look like the AI is typing the response.

In the end it was not used for that, but I did realise that I COULD used this to send more frequent position updates in FollowMe - so ineffect this is a PoC for my OWN application.

Before writing any code

Add A and AAAA records for a subdomain api.paul.sullivan.za.org and get certificates for that domain

apt-get install certbot

I already have a nodejs environment that I use on a VM.

Server Side Code

        streaming = true;
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
res.setHeader('Cache-Control', ' no-cache');
res.setHeader('Access-Control-Allow-Origin', 'https://paul.sullivan.za.org');
res.setHeader('Access-Control-Allow-Headers', '*');
res.setHeader('Transfer-Encoding', 'chunked');

let running = false;

let intrvl = setInterval(function(){
if (!running) {
running = true;
getma().then(function(r) {
running = false;
res.write(r+"\n");
});
}
}, 800);

let tmout = setTimeout(function(){
clearInterval(intrvl);
res.end('done');
}, 120000);

req.on('close', function(err) {
console.log("**CLOSED**");
clearInterval(intrvl);
clearTimeout(tmout);
res.end('{"r": "done"}');
});

break

This deals with CORS. And I found the "charset=UTF-8" was necessary.

getma() looks like this:

getma = function() {
  return new Promise(function(resolve, reject){
    fs.readFile('/var/run/current', 'utf8', (err, data) => {
      if (err) {
        console.error(err);
        return;
      }
      resolve(data);
    })
  })
}

I did run into a timezone issue with mysql2 timestamp dates from the DB server (in UTC) where being returned an hour out to the App server (in BST) this was fixed by forcing:

timezone: '+00:00'

If the client closes the page (sends a TCP RST) or reloads it, it is important to clear the Interval and Timeout or they will keep running - consuming reasource and sending it nowhere.

 

Client Side Code

To see if the client side is receiving data:

setInterval(checkXMLHttpRequest, 2000);

function checkXMLHttpRequest() {
if ((xhr.readyState === 0) || ((o.r == "done") && (xhr.readyState === 4))) {
xhr.abort();
start = 0;
xhr = new XMLHttpRequest();
xhr.addEventListener("progress", handleEvent);
xhr.open("GET", "https://api.paul.sullivan.za.org/FollowMe", true);
xhr.send();
}
}

if the HttpRequest is not running or the (finite) stream of server data is complete then a new request is made. 

Notes & Caveats

  1. The responseText property grows and grows so I've set up a finite amout of time the server will send data.
  2. I've noticed that if this page is loaded by browsers on PCs with certain security software the request comes from 161.69.71.25 (not the device IP).  The user agent presented is the one from the browser, but the CORS check is unable to complete.

Live power consumption

This is a live feed of (real) current draw on my mains ciruit.

Live Current consumption:

(Re)starts: .

I used a Youtube source to build the physical current clamp and interface it using I2C.