How many http connections can be open to the agent: long-polling

Thanks to this code from beardedinventor I am making good progress with the agents and a web page for my temperature-controlled oven.

WEB–>AGENT–>DEVICE–>AGENT–>WEB COMMUNICATION?

one thing that I have tried is to open the controlling web page on two different browsers at the same time. According to my code the http result objects would be entered into a table and both requests would be responded to upon a new event from the device. however, I cannot get that to work. The agent seems to never hold more than one request. Maybe it is a limitation of the agent? I think the limitation would be ok but I would like to understand it better.

my code works just great if there is only one browser connected.

For backround:

my imp device is reading temperature of the oven and I desire relatively fast feedback so I want to avoid polling from my web page with a timer.

The imp device sends a message to the imp agent which in turn sends the response to the currently open http get request. I always keep an open “get” and I think this technique is called long-polling. If two browser pages were opened - for example on my pc and on my android table - at the same time then two long-polling request would be open and both would simultaneously get this new temperature value.

That should work, the agent can definitely have multiple long polling calls open to it at once (not loads, but more than one). Did you add some debug logging?

Thanks for the quick response Hugo. I definitely added a bunch of logging notes but had not hit on the correct ones obviously. I thought about it through the night and I have a guess that my timestamps might be identical so subsequent events overwrite each other in the table. This could occur because I respond (push) to both browsers at the same time and each browser responds by immediately opening a new get request. Either that or the nearly simultaneous get requests cause other problems. I am going to try spacing out my pushes and see what happens now that I know the agent should allow this.

SOLVED
I figured this out. I had to add this code to the example from @beardedinventor in order to assure that I do not have duplicate timestamps and thus over-write a response object.

`
http.onrequest(function(request,res){

local t = time();
local testme=0;

while (testme==0){
  try{
    testme = HttpResponses[t];
    //server.log("NOTICE - time stamp is duplicated");
    t=t+1;
  }
  catch(error){
    testme=1;
    //server.log("SUCCESS - time stamp is unique");
  }
}

//…snip…//

HttpResponses[t] <- res;

//… the rest of this is snipped as it is shown elsewhere

`

The code relies on error trapping and this seems inelegant. It is one reason I posted it. maybe someone has a cleaner way to do this?

Even if I had put a delay into my http responses the timestamps would be duplicated in the http.onrequest function. I suppose something was being buffered somewhere? In my experiment two browsers have long-poll requests at the same time so when the Device sends something to the agent - the agent ~pushes this to both clients at the same time via the http response. Each browser in turn immediately opens another “get” request.

The effect had been that only one or the other active browser would get the live data and the other would be frozen because the http request was not answered and ultimately was going to time out.

now - the result is super cool! if I accidentally leave two browsers active with my page they are both simultaneously updated and eventually the button states will get pushed from one to the other and all.

here is the awesome code that I referenced from beardedinventor

here is the code I referenced

Hmm, I was thinking time was more precise than that. Good catch :slight_smile:

Two things you can do:

  1. Concatenate date().usecs() to time() to make a big string (it takes more than 1usec to generate the string):
    local key = format("%d%03d", time(), date().usec)

  2. If you wanted to cleanup your code above, you could change it to this:
    http.onrequest(function(req, resp) { local t = time(); while(t in HTTPResponses) t++; HTTPResponses[t] <- resp; // ... });

An even cleaner way might be to combine the two:
`function GetResponseKey() {
return format(format("%d%03d", time(), date().usec);
}

http.onrequest(function(req, resp) {
local key = GetResponseKey();
while(key in HTTPResponses) key = GetResponseKey();
HTTPResponses[key] <- resp;
// …
});`

That last example is pretty nice :slight_smile:

Aww peas, I just realized that breaks the timeout code…

Here’s new code that won’t:

`// Send timeout responses when required
function CleanResponses() {
// get current time
local now = time();

// loop through response queue
foreach(k, resp in HttpResponses) {
    // if request has timed-out
    if (now - resp.ts > TIMEOUT) {
        // log it, send the response, then delete it
        server.log("Request " + resp.ts + " timed-out");
        resp.r.send(408, "Request Timed-out");
        delete HttpResponses[k];
    }
}
// check for timeouts every seconds
imp.wakeup(1.0, CleanResponses);

} CleanResponses();

function GetResponseKey() {
return format(format("%d%03d", time(), date().usec);
}

http.onrequest(function(req, resp) {
local key = GetResponseKey();
while(key in HTTPResponses) key = GetResponseKey();
HTTPResponses[key] <- { ts = time(), r = resp };
// …
});`

Yeah… that should work (although I haven’t tested it)

love it! thanks much.

i will definitely try that.

I fully intend to share this project (again) when I get it running and tested.