Server saturation with Ajax calls - javascript

I'm using PHP over IIS 7.5 on Windows Server 2008.
My web application is requesting repeatedly with Ajax in the background 3 different JSON pages:
page 1 Every 6 seconds
page 2 Every 30 seconds
page 3 Every 60 seconds
They retrieve data related with the current state of some tables. This way I keep the view updated.
Usually I have no much trouble with it, but lately I saw my server saturated with hundreds of unanswered requests and I believe the problem can be due to a delay in one of the request.
If page1, which is being requested every 6 seconds, needs 45 seconds to respond (due to slow database queries or whatever), then it seem to me that the requests start getting piled one after the other.
If I have multiple users connected to the web application at the same time (or with multiple tabs) things can turn bad.
Any suggestion about how to avoid this kind of problem?
I was thinking about using some thing such as ZMQ together with Sockets.io in the client side, but as the data I'm requesting doesn't get fired from any user action, I don't see how this could be triggered from the server side.

I was thinking about using some thing such as ZMQ together with Sockets.io in the client side...
This is almost definitely the best option for long-running requests.
...but as the data I'm requesting doesn't get fired from any user action, I don't see how this could be triggered from the server side.
In this case, the 'user action' in question is connecting to the socket.io server. This cut-down example is taken from one of the socket.io getting started docs:
var io = require('socket.io')(http);
io.on('connection', function(socket) {
console.log('a user connected');
});
When the 'connection' event is fired, you could start listening for messages on your ZMQ message queue. If necessary, you could also start the long-running queries.

I ended up solving the problem following the recommendation of #epascarello and improving it a bit if I get no response in X time.
If the request has not come back, do not send another. But fix the serverside code and speed it up.
Basically I did something like the following:
var ELAPSED_TIME_LIMIT = 5; //5 minutes
var responseAnswered = true;
var prevTime = new Date().getTime();
setInterval(function(){
//if it was answered or more than X m inutes passed since the last call
if(responseAnsswered && elapsedTime() > ELAPSED_TIME_LIMIT){
getData()
updateElapsedTime();
}
}, 6000);
function getData(){
responseAnswered = false;
$.post("http://whatever.com/action.json", function(result){
responseAnswered = true
});
}
//Returns the elapsed time since the last time prevTime was update for the given element.
function elapsedTime(){
var curTime = new Date().getTime();
//time difference between the last scroll and the current one
var timeDiff = curTime - prevTime;
//time in minutes
return (timeDiff / 1000) / 60;
}
//updates the prevTime with the current time
function updateElapsedTime(){
prevTime = new Date().getTime();
}

This is a very bad setup. You should always avoid polling if possible. Instead of sending request every 6 seconds from client to server, send data from server to the clients. You should check at the server side if there is any change in the data, then transfer the data to the clients using websockets. You can use nodejs at the server side to monitor any changes in the data.

Related

ERR_INSUFFICIENT_RESOURCES ajax code for REAL-TIME CLOCK

I created a real-time clock that updates every second. When I run it locally I see no errors. However when I uploaded it to my web host I saw a lot of error messages in the console every time the AJAX code runs.
I think that's the reason why web host suspended my site, telling that my site has performed too many requests.
Here is my code:
$(document).ready(function() {
setInterval(function() {
$('#time').load('timewithdate.php')
}, 1000);
setInterval(function(){
$('#time2').load('time.php')
}, 1000);
})
I'm not sure what kind of answer you are looking for. Your code is a certain way to kill a server: it's making 2 calls to the server every second for each client (read more about DDOS). There is no need to make a server call, just use javascript to get the current time and format it the way you want. You can have something like this:
$(document).ready(function() {
setInterval(function() {
let curTime = new Date();
let date = curTime.toLocaleDateString();
let time = curTime.toLocaleTimeString();
$('#time').text(date + " " + time);
$('#time2').text(time);
}, 1000);
})
The error is because you have effectively DDOS'd your own server with 2 requests every second * number of concurrent clients. I would strongly suggest you remove these AJAX requests and perform the countdown on the client side.
If you're trying to keep the clocks in sync with server time, get the time from the server when the page loads, then add seconds to it on the client side. Do not use AJAX for this, and do not use AJAX polling in future. It's an anti-pattern.

Log a user out of a website when they put their computer to sleep

This is a bizarre one. We have a Laravel website, and on said site we have a timer per user, where they get 15 minutes of being inactive before being booted.
We do this through a timer that sits on the page in a react component, it works as we want it to, but now we have a new issue: If a user is logged in and shut the lid of their laptop the website should boot them. Banks do this, Schools and Universities do this, Government sites also do this. So it is possible, just not sure how.
We do use web sockets, using laravel-websockets library and Echo. What I would like to see happen is:
Once you close your laptop boot you to the login screen. So the next time you open the laptop and login, and see the browser you are on the login screen. It doesn't have to happen that quickly, but we need a way to send something to the front end basically telling them to refresh the page, once the session is killed, we set the session lifetime on laravel of 15 minutes.
Some people have suggested in other similar questions:
to create a custom web-socket handler
To compare the session cookie (in the browser) with the user cookie on the back end.
To have a timer running on the front end (we do, it just stops when you close the laptop lid)
The most popular one seems to be using web-sockets, listening for the user to disconnect and then boot them, which is fine and all, but then how do you send a request to a browser thats suspended to then boot them?
I have found requestIdleCallback() But again, I don't think this is what I want if I already have a heartbeat timer on the site. It also doesn't work in all browsers.
I am very lost here on how to accomplish this, the example I can give is:
Log in to your bank, put your computer to sleep, wait 15-20 minutes, awaken the computer, log in and see your bank now has you on the login screen. That's what I want. But I don't know how to accomplish that.
You cant send events to a "sleeping" browser from the back end, and while yes this would have to be a back end solution, how do you update the front end then, so that they are on the logout screen when they reawaken the laptop or computer?
First, let's expand on why Banking websites log you out after 15 minutes without activity. It's a PCI requirement for security.
PCI-DSS requirement 8.1.8:
8.1.8 If a session has been idle for more than 15 minutes, require the user to re-authenticate to re-activate the terminal or session.
In order to achieve this the solution is actually far more primitive than you imagine it to be. It neither requires the use of websockets nor knowing anything about the state of the client's machine (sleep or awake or otherwise). All that is required is knowing the time between the current request using that session and last request using the same session and ensuring they are not greater than 15 minutes apart. If they are the user is to be re-authenticated. If they aren't you may proceed with the request.
The "session timed out" message
You're probably then wondering (if it's that simple) how does the session timed out message appear when you put the computer to sleep and wake it back up. This part is deceptively simple.
When the computer is put to sleep the browser actually disconnects all TCP/IP connections which in turn shuts down the event loop in the javascript engine. So timers don't work. But when the browser wakes up again it attempts to refresh some things including the page itself. So when the page is refreshed the request goes back out to the server invoking the server to require the user re-authenticate.
However, this won't account for the javascript message modal (if that's what you're referring to) which some banking websites do. Also not all browsers do a hard refresh on the page in all scenarios. So another approach can be taken. Rather than have a timer in the browser that times out after 15 minutes you can simply store the page load time in javascript as a timestamp and have a 1 second interval time out that compares that timestamp to the computer's current timestamp. If they are more than 15 minutes apart, the session should be terminated.
window.onload = function() {
sessionStart = Date.now();
timer = setInterval(function() {
if (Date.now() - sessionStart > 15 * 60 * 1000) {
clearTimeout(timer);
alert("Session Timed out!");
window.location = "http://www.example.com/login";
}
}, 1000);
};
Even if the computer goes to sleep and the timer stops, the session will eventually time out on the server side (see section below for details) and when the computer wakes up again the timer with a 1 second interval will eventually startup again, invoking the message (as if the user timed out while the computer was asleep). The time lost between the time the computer went to sleep and the time the computer wakes up won't matter as the timestamp will remain in memory. The disconnect between the client and server is unimportant because they don't need to communicate this information in order for the session to be properly terminated on the server side. The server can do its own garbage collection and terminate the session without communication from the client (i.e asynchronously).
Believe it or not Banks don't care about activity inside of the client. They only care about request activity to the server. So if you're wondering how do they keep the session alive for greater than 15 minutes when the user is on the same page for that long, they simply send a AJAX request in the background to refresh the session after asking the user if they still want to continue.
This can be done in the same onload event callback we used earlier like so:
window.onload = function() {
sessionStart = Date.now();
timer = setInterval(function() {
if (Date.now() - sessionStart > 10 * 60 * 1000) {
if (confirm("Your session is about to timeout. Do you wish to continue?")) {
// send ajax request to refresh session TTL here
// reset the timer
sessionStart = Date.now();
}
} else if (Date.now() - sessionStart > 15 * 60 * 1000) {
clearTimeout(timer);
alert("Session Timed out!");
window.location = "http://www.example.com/login";
}
}, 1000);
};
Handling session termination on the server side
To handle the session termination on the server side there are several approaches. Depending on which one you use you will need different tactics. One is using PHP's default session handler and setting the session.max_lifetime to expire after 15 minutes (this deletes the session data entirely on the server side thus invalidating the client's cookie).
If you let the default session handler mechanism do it you can run into issues depending on which handler is used (files, memcached, redis, custom, etc).
With the files (default handler) the garbage collection happens in one of two ways:
Most Debian based systems do their own GC through a cron job (which works out great for your scenario)
Other distros let PHP's default GC mechanism handle it, which is based on a probabilistic outcome from each incoming request to PHP that checks file mtime's on session files and deletes those past their session.max_lifetime. The problem with this approach is that on low-traffic sites a session could potentially sit there on the server for a long time until enough requests come in (depending on the session.gc_probability score) to invoke the GC to clean up the session files.
With memcached and redis based handlers you don't have this problem. They will handle purging the memory automatically. Sessions may still remain in physical memory for a time past their lifetime, but the daemon will not be able to access them. If you're concerned about this bit for security you can encrypt your sessions at rest or find a key/value store that has stricter memory purging GC mechanism.
With a custom session handler you will have to build your own GC mechanism. Through SessionHandlerInterface you'd implement a gc method that hand you the session's maximum lifetime interval and you'd be responsible for verifying if the session has passed its lifetime based on this interval and do your garbage collection from there.
You can also setup a separate end-point that checks the session TTL (via asynchronous AJAX request on the client side) and sends back a response if the session has expired (forcing the javascript to re-authenticate the user).
UPDATE
Regarding the WebSocket request, I assume you're using Laravel WebSockets with pusher. Pusher.io does not support timeout, you can read this support article "Do you plan to add a connection timeout feature to the Channels pusher-js client library?". You can test it out if you enable Laravel debug mode (APP_DEBUG=true inside .env) and beggin laravel-websockets from terminal (php artisan websockets:serve) so you can see the output log events. If you try to close the laptop lid or set computer to hibernation mode (sleep), you won't see any messages regarding this event. You cannot do it with the pusher protocol. There is the member_removed Presence event, but that triggers only when you close the tab or you logout. Of course you can trigger your client custom event to the presence channel, but to do that you also need a timer setup to the client side and you'll have to create a service provider for the laravel-websockets server like this github issue "Exist a way to implement webhooks?".
Some people have suggested in other similar questions:
...
To have a timer running on the front end (we do, it just stops when you close the laptop lid)
That happens because client timers halt execution on hibernation, thus they continue from where they were before. But if you use a date variable to save the time, that variable will not get updated when the computer goes to hibernation, thus you'll know when it goes out from sleep by checking that date variable which in compare to current time will have significant difference and will be greater than the timer interval.
Implementing time logic in client
You can also see this implementation to this related Q/A: Can any desktop browsers detect when the computer resumes from sleep?
You can setup a timer in the client to run each minute. We won't rely on the timer interval, but instead that timer will check an outer scope date variable if the time span since last timer is greater than 15 minutes; if it is, then that means that the browser/JS halted execution for some reason, possibly hibernation of the device (sleep) and then you redirect the user to the logout route.
Example JS client code:
// Set a variable to check previous time
let clientSession = new Date;
// Setup the client session checking timer
let clientSessionTimer = setInterval(() => {
const now = new Date;
// Get how many seconds have passed since last check
const secondsSpan = (now - clientSession) / 1000;
// If the 1 minute timer has exceeded 15 minutes trigger logout and clear timer
if (secondsSpan > (60 * 15)) {
// For some reason JS halted execution, so we'll proceed with logging out
clearInterval(clientSessionTimer);
window.location.href = '/logout/session'
} else {
// The timer runs as it should, update the clientSession time
clientSession = now;
}
}, 1000 * 60);
You can check this simple example but using 1 second timer with 15 seconds logout here. Best to test it on a laptop with closing the lid and then open it again after 15 seconds a minute of two, because if you have many programs running, the computer takes some time to save memory state so to complete hibernation mode and halt execution.
Web Workers Example
You can even use Web Workers API to setup a web worker to be much safer:
Page JS code:
const logoutWorker = new Worker('logoutWorker.js');
logoutWorker.onmessage = function (ev) {
if (ev && ev.data === 'wakeup') {
logoutWorker.terminate();
// window.location.href = '/logout/session'
} else {
// The timer runs as it should, nothing to do
}
}
Web worker logoutWorker.js code:
let clientSession = new Date();
let clientSessionTimer = setInterval(() => {
const now = new Date;
const secondsSpan = (now - clientSession) / 1000;
if (secondsSpan > 15) {
postMessage('wakeup'); // Send a message wakeup to the worker page
clearInterval(clientSessionTimer); // Clear the timer
} else {
clientSession = now; // Update the clientSession timer variable
postMessage('update'); // And post a message to the page ONLY IF needed
}
}, 1000);
You can also check the Web Worker example with the same 15 seconds timer here.
So Idea is behind setInterval and Sockets,
setInterval is supported in most browsers and javascript WbsocketApi is supported in almost everybrowser.
Brief overview:
setInterval() - this function behaviour is following when your computer is at sleep/suspended/hibernate mode it is paused and when you are at awaken mode it resumes itself.
The following code does the following, at first(maybe at the same time but) it starts php server_socket listening to the connections,
than javascript websocket api sends current timestamp in Unix timestamp milliseconds in every 2 seconds you can have 1 second it is up to you.
after that php server socket is getting this time and checks if it has anything like previous time to compare, when the code is first instantiated php does not has anything like previous time to compare it to the time which was sent from javascript websocket, so php does nothing but saves this time in the session called 'prev_time' and waits for another time data to be recieved from javascript socket, so here begins second cycle.
when php server socket new time data from javascript WebsocketApi it checks it has anything like previous time to compare to this newly received time data, it means that php checks if session called 'prev_time' exists, as we are in the second cycle php discovers that it exists, grabs it's value and does following $diff = $new_time - $prev_time, $diff will be 2 seconds or 2000 miliseconds because remember our setInterval cycle happens in every 2 seconds and time format we are sending is in miliseconds,
than php checks if($diff<3000) if difference is less than 3000 if it is it knows that user is active, again you can manipulate this seconds as you wish, I choose 3000 because possible latency in the network which is almost impossible but you know I am always cautious when it comes to networks, so let's continue, when php determines that user is active php just resets 'prev_time' session with the value of $new_time which was newly received and just for testing purposes it sends message back to javascript socket,
but if $diff is more than 3000 it means that something paused our setInterval and there is only way it can happen and I think you already know what I am saying, so in the else logic of ( if($diff<3000) ) you can logout user by destroying specific session and if you want to redirect you can send some text to javacript socket and create a logic which will execute window.location = "/login" depending on the text, that's it here is the code:
First it is index.html file just to load javascript:
<html>
<body>
<div id="printer"></div>
<script src="javascript_client_socket.js"></script>
</body>
</html>
then it is javascript it is not really beautifully coded but you can figure out READ COMMENTS THEY ARE IMPORTANT:
var socket = new WebSocket('ws://localhost:34237'); // connecting to socket
// Open the socket
socket.onopen = function(event) { // detecting when connection is established
setInterval(function(){ //seting interval for 2 seconds
var date = new Date(); //grabing current date
var nowtime = Date.parse(date); // parisng it in miliseconds
var msg = 'I am the client.'; //jsut testing message
// Send an initial message
socket.send(nowtime); //sending the time to php socket
},2000);
};
// Listen for messages
socket.onmessage = function(event) { //print text which will be sent by php socket
console.log('php: ' + event.data);
};
// Listen for socket closes
socket.onclose = function(event) {
console.log('Client notified socket has closed', event);
};
now here is part of php code, don't worry down there is full code too but this part is actually what does above mentioned jobs you will meet other functions too but they are for decoding and working with javascript sockets so it is actual thing right here READ COMMENTS THEY ARE IMPORTANT:
<?php
$decoded_data = unmask($data /* $data is actual data received from javascript socket */); //grabbing data and unmasking it | unmasking is for javascript sockets don't mind this
print("< ".$decoded_data."\n");
$response = strrev($decoded_data);
$jsTime = (int) $decoded_data; /* time sent by javascript in MILISECONDS IN UNIX FORMAT */
if (isset($_SESSION['prev_time'])) { /** check if we have stored previous time in the session */
$prev_time = (int) $_SESSION['prev_time']; /** grabbing the previous time from session */
$diff = $jsTime-$prev_time; /** getting the difference newly sent time and previous time by subtracting */
print("$jsTime - $prev_time = $diff"); /** printing the difference */
if($diff<3000){ /** checking if difference is less than 3 second if it is it means pc was not at sleep
*** you can manipulate and have for example 1 second = 1000ms */
socket_write($client,encode("You are active! your pc is awakend"));
$_SESSION['prev_time'] = $jsTime; /** saving newly sent time as previous time for future testing whcih will happen in two seconds in our case*/
}else { /** if it is more than 3 seconds it means that javascript setInterval function was paused and resumed after 3 seconds
** So it means that it was at sleep because when your PC is at sleep/suspended/hibernate mode setINterval gets pauesd */
socket_write($client,encode("You are not active! your pc is at sleep"));
$_SESSION['prev_time'] = $jsTime;
}
}else { /** if we have not saved the previous time in session save it */
$_SESSION['prev_time'] = $jsTime;
}
print_r($_SESSION);
?>
And here is the full code of php:
<?php
//Code by: Nabi KAZ <www.nabi.ir>
session_abort();
// set some variables
$host = "127.0.0.1";
$port = 34237;
date_default_timezone_set("UTC");
// don't timeout!
set_time_limit(0);
// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0)or die("Could not create socket\n");
// bind socket to port
$result = socket_bind($socket, $host, $port)or die("Could not bind to socket\n");
// start listening for connections
$result = socket_listen($socket, 20)or die("Could not set up socket listener\n");
$flag_handshake = false;
$client = null;
do {
if (!$client) {
// accept incoming connections
// client another socket to handle communication
$client = socket_accept($socket)or die("Could not accept incoming connection\n");
}
$bytes = #socket_recv($client, $data, 2048, 0);
if ($flag_handshake == false) {
if ((int)$bytes == 0)
continue;
//print("Handshaking headers from client: ".$data."\n");
if (handshake($client, $data, $socket)) {
$flag_handshake = true;
}
}
elseif($flag_handshake == true) {
/*
**** Main section for detectin sleep or not **
*/
if ($data != "") {
$decoded_data = unmask($data /* $data is actual data received from javascript socket */); //grabbing data and unmasking it | unmasking is for javascript sockets don't mind this
print("< ".$decoded_data."\n");
$response = strrev($decoded_data);
$jsTime = (int) $decoded_data; /* time sent by javascript in MILISECONDS IN UNIX FORMAT */
if (isset($_SESSION['prev_time'])) { /** check if we have stored previous time in the session */
$prev_time = (int) $_SESSION['prev_time']; /** grabbing the previous time from session */
$diff = $jsTime-$prev_time; /** getting the difference newly sent time and previous time by subtracting */
print("$jsTime - $prev_time = $diff"); /** printing the difference */
if($diff<3000){ /** checking if difference is less than 3 second if it is it means pc was not at sleep
*** you can manipulate and have for example 1 second = 1000ms */
socket_write($client,encode("You are active! your pc is awakend"));
$_SESSION['prev_time'] = $jsTime; /** saving newly sent time as previous time for future testing whcih will happen in two seconds in our case*/
}else { /** if it is more than 3 seconds it means that javascript setInterval function was paused and resumed after 3 seconds
** So it means that it was at sleep because when your PC is at sleep/suspended/hibernate mode setINterval gets pauesd */
socket_write($client,encode("You are not active! your pc is at sleep"));
$_SESSION['prev_time'] = $jsTime;
}
}else { /** if we have not saved the previous time in session save it */
$_SESSION['prev_time'] = $jsTime;
}
print_r($_SESSION);
/*
**** end of Main section for detectin sleep or not **
*/
}
}
} while (true);
// close sockets
socket_close($client);
socket_close($socket);
$client = null;
$flag_handshake = false;
function handshake($client, $headers, $socket) {
if (preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match))
$version = $match[1];
else {
print("The client doesn't support WebSocket");
return false;
}
if ($version == 13) {
// Extract header variables
if (preg_match("/GET (.*) HTTP/", $headers, $match))
$root = $match[1];
if (preg_match("/Host: (.*)\r\n/", $headers, $match))
$host = $match[1];
if (preg_match("/Origin: (.*)\r\n/", $headers, $match))
$origin = $match[1];
if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
$key = $match[1];
$acceptKey = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
$acceptKey = base64_encode(sha1($acceptKey, true));
$upgrade = "HTTP/1.1 101 Switching Protocols\r\n".
"Upgrade: websocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Accept: $acceptKey".
"\r\n\r\n";
socket_write($client, $upgrade);
return true;
} else {
print("WebSocket version 13 required (the client supports version {$version})");
return false;
}
}
function unmask($payload) {
$length = ord($payload[1]) & 127;
if ($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
}
elseif($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
}
else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
}
$text = '';
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
return $text;
}
function encode($text) {
// 0x1 text frame (FIN + opcode)
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if ($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)$header = pack('CCS', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCN', $b1, 127, $length);
return $header.$text;
}
NOTE READ IT:
$new_time variable is $jsTime in Code
create folder and just copy and paste this in files run php socket with the command: php -f server_socket.php go to the localhost and test it open console to see messages it will say "you are active" or "you are not active"(when you come from sleep); your executin will happen when user will come from sleep not when they are at sleep cause at that moment everything is cached in pagefile(windows) or in swap(linux)
I think i have a idea, you've discussed a lot about how bank login/logout system works.
Case-1: Access of webpage to user for unlimited time if user is active
Whenever user logged in, Start a timer on your backend(set the time limit whatever you want), let's say 15 mins. Now what it means?? It means if user doesn't perform any activity on the webpage, then we'll log him/her out.
Now, from front you can send the user activity to your backend(could be send using socket or long polling), which will basically reset the timer and user can use the webpage actively for whatever time they want.
If user put their PC on sleep, timer won't reset and you can invalidate the session once timer ends.
If you want to invalidate the user session as soon as they put their pc on sleep, you may set the limit of the session validate time. For example, when user logs-in, we'll create the session which will be valid only for 10 secs, and once we receive the user activity request, we can reset the timer and provide a new session key.
I hope this helps you. Let me know if you've any question.
If you update a timestamp in the user record with each heartbeat received from the client (e.g. a column called "lastHeartbeat"), you could use your database's Scheduled Event functionality to log them off without any need for the browser to be active on the client side. See Working with MySQL Scheduled Events
DELIMITER $$
DROP EVENT IF EXIST autoLogOff_event;
CREATE EVENT autoLogOff_event
ON SCHEDULE EVERY 1 MINUTE
STARTS CURRENT_TIMESTAMP
ENDS CURRENT_TIMESTAMP + INTERVAL 24 HOUR
DO
BEGIN
UPDATE users SET logOffTime = NOW()
WHERE lastHeartbeat < NOW( ) - INTERVAL 20 MINUTE
END
END $$
DELIMITER ;
I wrote a script to detect if the machine went to sleep. Idea is that when the machine is in sleep mood all all scripts will stop. Therefore if we keep track of the current time within a timeInterval. Everytime the timeInterval triggers the current time minus(-) the new time should be close enough to the timeInterval. Therefor if we want to check if timer was idle for X time, we can check if the time difference is more than X.
Example blow checks if the computer was put to sleep for more than 15s. Please note when you put the computer to sleep it make take about another extra 15s to idea all processors. (When tested on MY PC).
(function() {
this.SleepTimer = function() {
// console.log('sleep timer initiated');
// Create global element references
this.sleepTimer = null;
this.maxTime = null;
this.curDate = null;
this.newDate = null;
this.timer = null;
this.timeInterval = 1000;
this.sleepTimer = new CustomEvent("sleepTimer", {
"detail": {
"maxTime":this.maxTime,
"idelFor": this.newDate - this.curDate,
"timer": this.timer
}
});
// Define option defaults
var defaults = {
maxTime: 10000,
timeInterval: 1000,
autoStart: true,
console: false,
onStart: null,
onIdel: null
}
// Create options by extending defaults with the passed in arugments
if (arguments[0] && typeof arguments[0] === "object") {
this.options = extendDefaults(defaults, arguments[0]);
}
if (this.options.timeInterval) {
this.timeInterval = Math.max(1000, this.options.timeInterval);
this.maxTime = Math.max(this.options.maxTime, 10000);
} else {
this.options = defaults;
}
if(this.options.autoStart === true) this.start()
// Utility method to extend defaults with user options
}
function extendDefaults(source, properties) {
var property;
for (property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
SleepTimer.prototype.start = function(){
var _ = this;
this.options.onStart()
this.curDate = Date.now();
this.timer = setInterval(function() {
_.newDate = Date.now();
var diff = _.newDate - _.curDate;
// for debugging
if(_.options.console && diff > _.timeInterval){
console.log('Your PC was idel for ' + diff / 1000 + 's of ' + _.maxTime /1000 + 's. TimeInterval is set to ' + _.timeInterval / 1000 + 's');
}
if (diff < _.maxTime) {
_.curDate = _.newDate;
} else {
_.options.onIdel();
// alert('You have been idle for ' + diff / 1000 + 's');
clearTimeout(_.timer);
}
}, this.timeInterval); // seconds
}
}());
var sleepTimer = new SleepTimer({
maxTime: 15000,
console: true,
onStart: function(){
console.log('sleepTimer started.');
},
onIdel: function(){
alert('Your session expired! Please login again.');
}
});
I have implemented the exact same requirement using AWS Cognito, with Lambda Authorizers, & Redis, I can't share the code at this stage but I can tell you everything how it is implemented with these components, the same concepts can be used with other non AWS components.
Firstly with implementing an inactivity logout, you will have to do it server side, as if somebody simply turns off their computer, the front-end website would not log them out. I used the concept of ACTIVE users. When users successfully authenticates, I store with a TTL of 15 minutes in Redis an entry with a key of their username & a value of ACTIVE (it can be username+sessionid if you want to allow multiple sessions for a given user at the same time).
In my custom Authorizers when a user is ACTIVE & they have a valid Token, I grant them access to the protected resource AND most importantly, I do another put in Redis with the username & ACTIVE.
Whenever the user logs out, I log them out in my identity management solution (Cognito) & I mark them as INACTIVE. Note that if a user does not hit the API within 15 minutes they will no longer have an entry of ACTIVE against their username and will not be able to access the API anymore & have to sign in again, for which they will be redirected to do.
There are many things to consider with this approach, for one thing often Authorizers cache results for some amount of time, and if say you cache the result for 5 minutes as an example, then you user might be logged off in 10 minutes as your user could hit the cache instead of the Authorizer which would not refresh the ACTIVE entry.
It's also important that you make sure whatever you use to store if a given user is ACTIVE is highly available and will recover quickly in the event of a failure.
The approach of using a cache store this way is similar to how token invalidation is retrofitted to stateless authorization protocols such as OAuth2.
We've been using this approach for a few months now, it seems to work fine for us, it can be a bit of an annoying requirement to handle, I'd had expected in AWS world that there would be a ready to use out of the box solution for this but there was none to speak of.

Push notification server backend for iOS and Android Interval Poll methods

I am writing a NodeJS push notification server for iOS and Android. Currently, once I get the device tokens I save them in a local MongoDB database and then fire notifications when there is a change. The problem is, that the database where the "change"(information is added) happens isn't my own. It's a third party's server. So the way I am aware of the change in the server is by using an API provided by the third party. My current solution is using setTimeout to make a request every X minutes to check for a change and then firing a notification based on that. The shorted interval is 10 minutes and the longest one is 1 hour. I change the intervals
dynamically through out the day based on time. My question(s) is (are),
1.) Is the setTimeout method the best possible solution to this problem? If not what else can I use?
2.) Is there any way I can implement Web Sockets in this scenario?
3.) If setTimeout is the only option, what kind of problems should I expect to run into?
This what the current function looks like
function start_notifications_server_driver() {
if(current_user_info.num_sent <= current_user_info.frequency){
//I removed the interval object on here because it's quite large and would take up space here
for(var i = 0; i < intervals.length; i++){
if(check_if_time_between(intervals[i])){
if(dev_mode) console.log("Returned true for", intervals[i]);
temp_interval = intervals[i].frequency;
break;
}
}
check_updates_for_channels_driver();
setTimeout(start_notifications_server_driver, temp_interval);
console.log("Time Interval changed to now", temp_interval);
}
}

proper calculation for javascript time differences from server roundtrip

trying to get difference in time handled for a game, but I haven't been able to get the math right (My weak point). Heres what I have so far:
function(timeSentToServer, serverTime) {
var difference = serverTime-timeSentToServer
var roundTripTime = Date.now()-timeSentToServer;
var responseTime = roundTripTime-difference;
timeDifference = responseTime;
}
timeSentToServer is the Date.now() when the request was sent. This is required due to multiple requests, and this function isn't within the scope of the function that sends the request.
serverTime is the time when the server received the request
the timeDifference should be a value that I can add to Date.now() to match it up with the current serverTime (probably a few milliseconds off, but thats acceptable)
however, I am running into an issue where one device Im testing against is just one second ahead of the server, and its throwing the whole calculation out of whack
e.g.: currentTime: 968952 sentTime: 968834 serverTime: 967742 difference: 1210
which results in being even more ahead of the server.
will Date.now()-difference be the solution I'm looking for? and also, is there any other articles I can read on the subject, and any different solutions?

How to have a timer which cannot be modified in javascript?

Basically, I am designing a quiz application with limited time. Use selects answer to a question and the next question loads using an Ajax request. All questions must be answered within a time frame of, say 2 minutes.
A clock ticks away to show how much time is left and as soon as it hits 0, results are shown. Now since the timer will be implemented using window.setTimeout(), it is possible that the value of timer variable be modified using an external bookmarklet or something like that. Anyway I can prevent this? I think this is implemented on file sharing sites like megaupload. Any forgery on the timer variable results in request for file being rejected.
Have .setTimeout() call an AJAX method on your server to synch time. Don't rely on the client time. You could also store the start time on the server for a quiz, and then check the end time when the quiz is posted.
You need to add a validation in your server side. When the client want to load the next question using an Ajax request, check whether deadline arrived.
The timer in client side js just a presention layer.
If the function runs as a immediately called function expression, then there are no global variables and nothing for a local script to subvert. Of course there's nothing to stop a user from reading your code and formulating a spoof, but anything to do with javascript is open to such attacks.
As others have said, use the server to validate requests based on the clock, do not rely on it to guarantee anything. Here's a simple count down that works from a start time so attempts to dealy execution won't work. There are no global variables to reset or modify either.
e.g.
(function (){
// Place to write count down
var el = document.getElementById('secondsLeft');
var starttime,
timeout,
limit = 20; // Timelimit in seconds
// Function to run about every second
function nextTick() {
var d = new Date();
// Set start time the first time
if (!starttime) starttime = d.getTime();
var diff = d.getTime() - starttime;
// Only run for period
if (diff < (limit * 1000)) {
el.innerHTML = limit - (diff/1000 | 0);
} else {
// Time's up
el.innerHTML = 0;
clearTimeout(timeout);
}
}
// Kick it off
timeout = window.setInterval(nextTick, 1000);
}());

Categories

Resources