How to send Server-Sent Event Notifications when needed, not periodically, to a specific browser session? - html

I want to use SSE's to send data from the server to a specific individual client that is logged in to my web-application, when their session timeout 'clock' is about to expire or when there is a message that is supposed to be sent to all 'connected' users, similar to a UNIX wall command, such as 'the system will be unavailable in 5 minute, please complete your transactions and log-off.'
The examples that I've seen at w3schools and MSDN, automatically/periodically send messages to the connected clients, such as transmitting the server's time.
I want to avoid polling the server from the client with ajax requests for questions like 'Am I still logged-in?' (session timeout expired) or 'What, if any, is the the server message?' The first is specific to the user and the second is for all current users.
Can this be done, and is there an example of how to do this?
Thanks
Since posting this question, I was partially successful in cobbling a SSE server/client demo to work. I say, partly successful, because I've not been able to get the different durations to work using the retry option. If I leave the option out, the client gets a message every three seconds. If I include the option, then the client only gets one message and hages trying to connect with the server. Here is the PHP server-side code:
<?php
header( 'Content-Type: text/event-stream' );
header( 'Cache-Control: no-cache' );
// data: {"msg": "First message"}\ndata: {"msg": "second message"}\n\n
$r = mt_rand( 1, 10 ) * 1000;
$m = "retry interval: ${r}";
$r *= 1000;
$t = date( 'r' );
// echo "retry: ${r}" . PHP_EOL; // <==== With out this, it works!
echo "data: {\"message\" : \"${m}\"}" . PHP_EOL;
echo "data: {\"message\" : \"The server time is: ${t}\"}" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
?>
Here is the client-side code:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Server-Sent Events Example</title>
</head>
<body>
<p>
<button onclick="source.close();">Stop!</button>
</p>
<h1>Getting server updates</h1>
<div id="result"></div>
<script>
// Source: https://www.html5rocks.com/en/tutorials/eventsource/basics/
if( typeof( EventSource ) !== 'undefined' ) {
// 'http://symfony.dev.iambreakinout.com/SSE_Server.php'
var source = new EventSource( 'SSE_Server.php' );
source.addEventListener(
'message',
function( event ) {
if( event.origin != window.location.origin ) {
alert( 'Cross-site scripting: message origin did not match:' +
'\r\n' +
'expected origin: ' + window.location.origin + '\r\n' +
'actual origin: ' + event.origin );
return;
}
else {
var data = [];
for( var i = 0, b = 0, e = 0, d = '';; ++i, b = e + 1 ) {
e = event.data.indexOf( "\n", b );
s = ( ( e > -1 )
? event.data.substr( b, e )
: event.data.substr( b ) );
data[ i ] = JSON.parse( s ).message;
if( e === -1 ) break;
}
document.getElementById( 'result' ).innerHTML +=
data.join( '<br>' ) + '<br>';
}
},
false );
source.addEventListener(
'open',
function( event ) {
// Connection was opened.
document.getElementById( 'result' ).innerHTML += 'Open<br>';
},
false );
source.addEventListener(
'error',
function( event ) {
var readyState;
//
// The closed ready state is seen when an error event occurs, but
// the rest are shown here as a reminder to me of the defined
// ready state constant values.
//
switch( event.currentTarget.readyState ) {
case EventSource.CONNECTING: readyState = 'Connecting'; break;
case EventSource.OPEN: readyState = 'Open'; break;
case EventSource.CLOSED: readyState = 'Closed'; break;
default: readyState = '?'; break;
}
document.getElementById( 'result' ).innerHTML += readyState +
'<br>';
},
false );
}
else {
document.getElementById("result").innerHTML =
'Sorry, your browser does not support server-sent events...';
}
</script>
<button onclick="source.close();">Stop!</button>
</body>
</html>
Without changing the client code and allowing the echo "retry: ${r}" . PHP_EOL; statement to specify a retry duration causes the output to stop after Connecting i shown.
Getting server updates
Open
retry interval: 3000
Connecting
What am I doing wrong or not doing to allow for the retry option to work?
Thanks again
OK, the code as stands is fine, but it really should not have had the first 'times 1000' when it generated the random number of interval. (duh!).

You need to keep your active users in some data structure to be able to send events to them.
Below is a simple commented example on NodeJS (based on https://github.com/mchaov/simple-sse-nodejs-setup/):
const http = require('http');
const msg = " - Simple SSE server - ";
const port = 5000;
// Create basic server
http.createServer(function (request, response) {
// answer only to event stream requests
if (request.headers.accept && request.headers.accept == 'text/event-stream') {
// check if the resource is what we want
// => http://domain.ext/sse
// store the user into your data structure
if (/sse/gim.test(request.url)) {
sendSSE(request, response);
}
}
else {
// if not just return that you are online and a string of text
response.writeHead(200);
response.write('Welcome to ' + msg + '# :' + port);
response.end();
}
}).listen(port);
// Setup the SSE "service" :)
// You need to cache the "response" object per user, and then you can use the functions below to send messages whenever you desire. You can have a collection that holds all the references to the response object and decide if you want to send message to one or all.
function sendSSE(request, response) {
// Setup headers
// For ease of use: example for enabled CORS
response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
// enabling CORS
'Access-Control-Allow-Origin': "*",
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
});
var id = (new Date()).toLocaleTimeString();
// send first message
constructSSE(response, id, (new Date()).toLocaleTimeString());
// send message every second and a half
setInterval(function () {
constructSSE(response, id, (new Date()).toLocaleTimeString());
}, 1500);
}
// setup simple message
// call this function with proper response you took from data structure with active users to send message to the specific user
function constructSSE(response, id, data) {
response.write('id: ' + id + '\n');
// response.write('event: ' + 'logout' + '\n'); // check this
response.write("data: " + msg + port + '\n\n');
}

Related

Uncaught TypeError: Cannot read properties of null (reading 'filter')

I'm seeing these two errors:
Uncaught TypeError:
Cannot read properties of null (reading 'filter')
along with this - Uncaught ReferenceError: $ is not defined
I've tried running it a few times and it gave me the same answer each time. I can't seem to figure out where the issue is..
This is the code:
function scrollToBottom(){
bottom = document.body.scrollHeight;
current = window.innerHeight+ document.body.scrollTop;
if((bottom-current) >0){
window.scrollTo(0, bottom);
//wait a few
setTimeout ( 'scrollToBottom()', 1500 );
}
(function() {
var placeholderString,
placeholderWords,
contactName,
companyName,
customNote,
jobDescription,
jobUrl,
jobInfo,
str = '';
$('.job_listings')
.filter(function(_, elem) {
return ($(elem).attr('data-force-note') );
})
.each(function(_, elem) {
// Find the URL for each job listing.
$(elem)
.find('.top a[href]')
.each( function(idx, value) { str += $(value).attr('href') + "\n"; });
// Get the company and contact info
placeholderString = $(elem)
.find('.interested-note').attr('placeholder');
// Split placeholder string into words:
placeholderWords = placeholderString.split(' ');
// Grab name of recruiter/contact
contactName = placeholderWords[4];
// Grab company name
companyName = $(elem).find('.startup-link').text();
// Build personalized note
customNote = "Hi " + contactName + "! Would love to join " + companyName + " using my diverse set of skills. Let's chat!";
// .header-info .tagline (text)
jobDescription = $(elem).find('.tagline').text();
// .header-info .startup-link (href attr)
jobUrl = $(elem).find('.startup-link').attr('href');
// Compile and format job information
jobInfo = companyName + '\n' + jobDescription + '\n' + str + '\n\n';
// Get job data for your own records
console.log(jobInfo);
// Comment this out to verify your customNote
// console.log(customNote + '\n');
// Add your custom note.
// Comment these lines out to debug
$(elem)
.find('.interested-note').text( customNote );
//Comment these lines out to debug
$(elem)
.find('.interested-with-note-button')
.each( function(idx, button) { $(button).click(); });
});
// Print all of the company and job info to the console.
return jobInfo;
})();
};
scrollToBottom();
Is anyone able to help me here?
Thank you!

How to make a user on my website ping a specific ip address?

I'm wondering how website like this one : https://ping.eu/ping/ manage to make our ip ping an other ip and get the result.
Someone have an idea ?
Thanks
It Doesn't. A PHP script(on the server) will most likely do it with "PHP Sockets". Have a look at
this: https://www.php.net/manual/en/sockets.examples.php
Else it could use exec() function, but that would be a security flaw.
So to answer your question: The website will ping the IP address not the 'client'
If you want to ping a server, i.e. an actual web address/URL like www.google.com, you can look at this JSFiddle http://jsfiddle.net/GSSCD/203/ or GitHub repository https://github.com/jdfreder/pingjs.
Here's some code from the JSFiddle:
function Pinger_ping(ip, callback) {
if(!this.inUse) {
this.inUse = true;
this.callback = callback
this.ip = ip;
var _that = this;
this.img = new Image();
this.img.onload = function() {_that.good();};
this.img.onerror = function() {_that.good();};
this.start = new Date().getTime();
this.img.src = "http://" + ip;
this.timer = setTimeout(function() { _that.bad();}, 1500);
}
}
Another way to ping a server/web address is to use JavaScript and the XMLHttpRequest() function it supports:
HTML:
<div id="result"></div>
JavaScript:
function http_ping(fqdn) {
var NB_ITERATIONS = 4; // number of loop iterations
var MAX_ITERATIONS = 5; // beware: the number of simultaneous XMLHttpRequest is limited by the browser!
var TIME_PERIOD = 1000; // 1000 ms between each ping
var i = 0;
var over_flag = 0;
var time_cumul = 0;
var REQUEST_TIMEOUT = 9000;
var TIMEOUT_ERROR = 0;
document.getElementById('result').innerHTML = "HTTP ping for " + fqdn + "</br>";
var ping_loop = setInterval(function() {
// let's change non-existent URL each time to avoid possible side effect with web proxy-cache software on the line
url = "http://" + fqdn + "/a30Fkezt_77" + Math.random().toString(36).substring(7);
if (i < MAX_ITERATIONS) {
var ping = new XMLHttpRequest();
i++;
ping.seq = i;
over_flag++;
ping.date1 = Date.now();
ping.timeout = REQUEST_TIMEOUT; // it could happen that the request takes a very long time
ping.onreadystatechange = function() { // the request has returned something, let's log it (starting after the first one)
if (ping.readyState == 4 && TIMEOUT_ERROR == 0) {
over_flag--;
if (ping.seq > 1) {
delta_time = Date.now() - ping.date1;
time_cumul += delta_time;
document.getElementById('result').innerHTML += "</br>http_seq=" + (ping.seq-1) + " time=" + delta_time + " ms</br>";
}
}
}
ping.ontimeout = function() {
TIMEOUT_ERROR = 1;
}
ping.open("GET", url, true);
ping.send();
}
if ((i > NB_ITERATIONS) && (over_flag < 1)) { // all requests are passed and have returned
clearInterval(ping_loop);
var avg_time = Math.round(time_cumul / (i - 1));
document.getElementById('result').innerHTML += "</br> Average ping latency on " + (i-1) + " iterations: " + avg_time + "ms </br>";
}
if (TIMEOUT_ERROR == 1) { // timeout: data cannot be accurate
clearInterval(ping_loop);
document.getElementById('result').innerHTML += "<br/> THERE WAS A TIMEOUT ERROR <br/>";
return;
}
}, TIME_PERIOD);
}
But, of course, these are web addresses, not IP addresses, so I'm not sure if that's what you're aiming for. I'm also not sure if you're looking for the amount of time spent to get the connection and the number of packets and bytes sent and received, or if you just want to validate the connection. The above code does all of those things. For IP addresses, you can try an AJAX request to ping a server, which only sees if there is a connection using YOUR SERVER'S IP address, NOT THE USER'S CLIENT, like this:
client --AJAX-- yourserver --ICMP ping-- targetservers
You could also try:
Using a Java applet with isReachable
Writing a server-side script which pings, and using AJAX to communicate to your server-side script
You might also be able to ping in Flash (using ActionScript)
One last hypothetical and unorthodox way to get an IP address is to inspect and view the source of the website you mentioned and copy some code, mostly JavaScript, and test it on your end and try to implement it.

Getting response with NodeJS request module

I just started using the twitch kraken api and I have a few questions.
Whenever I attempt to get a JSON object there is no response. I am attempting to run this function through Amazon AWS Lambda, and don't have access to a console.
In the code below my callback function will always print out "SUCCESS got streamers ERROR". I am pretty certain right now the "ERROR" comes from my initial setting of result.
How come result does not get changed into the proper JSON?
I have used postman and it returns the proper thing with the query and param, and headers:
function getJSON(callback){
var result = "ERROR";
request.get(url(games[0]),function(error,response,body){
console.log("requested for url: " + url(games[0]));
var d = JSON.parse(body);
result = d.streams[0];//.channel.display_name;
// for(var i = 0; i < limit; i++){
// streamers.push(d.streams[i].channel.display_name)
// }
streamers.push(result);
});
if (streamers.length < 0){
callback("ERROR");
}else{
callback("SUCCESS got streamers " + result);
}
}
function url(game){
return {
url: "https://api.twitch.tv/kraken/streams/",//twitchlimit,
qs : {
'game' : 'overwatch',
'limit' : 2
},
headers: {
'Client-ID': clientID,
'Accept': 'application/json',
'Accept-Charset': 'utf-8',
}
};
}
I think your streamers code
if (streamers.length < 0){
callback("ERROR");
}else{
callback("SUCCESS got streamers " + result);
}
should be included in the request callback because currently it's not waiting for the request to finish, it's just carrying on so therefore the value of result will not change. Also the array length cannot be less than 0 so it will always go to the else and say "SUCCESS got streamers ERROR"
Thank you guys for the suggestions. I did have a few oversights and attempted to fix them.
I have implemented you suggestions and it seems to have worked a bit. I ended up putting the json.parse into a try/catch block, and moved the if/else statements inside the getJSON method. However, now I don't get any output.
This is how I am invoking the getJSON method:
function handleGameResponse(intent,session,callback){
//gets the game
var game = intent.slots.game.value;
if (!games.includes(game)){
var speechOutput = "You asked for: " + intent.slots.game.value;
//var speechOutput = "You asked for: " + games[game] + " That game is currently not an option. These are your current options: " + arrayToString(games)
var repromptText = "Please ask one from the current options.";
var header = "Invalid Game";
}else {
getJSON(function(data){
if(data !== "ERROR"){
var speechOutput = data; //capitalizeFirst(game) + " top three streamers are: " + arrayToString(streamers) + '.';
var repromptText = "Do you want to hear more about games?";
var header = capitalizeFirst(game);
}else{
var speechOutput = "I'm sorry, something went wrong and I could not get the streamers.";
}
//speechOutput = data;
});
//speechOutput = games[0] + " games[0], game= " + game; //this executes so the getJSON isn't executing
}
var shouldEndSession = false;
callback(session.attributes,buildSpeechletResponse(header,speechOutput,repromptText,shouldEndSession));
}
Does the above execute the same way? As in the shouldEndSession and callback execute before the getJSON has time to give a response?
For ref, this is the getJSON method now:
function getJSON(callback){
var result = "ERROR";
request.get(url(games[0]),function(error,response,body){
try{
var d = JSON.parse(body);
} catch (err){
callback("Sorry, something seems to have malfunctioned while getting the streamers");
}
result = d.streams[0].channel.display_name;
// for(var i = 0; i < limit; i++){
// streamers.push(d.streams[i].channel.display_name)
// }
streamers.push(result);
if (streamers.length <= 0){
callback("ERROR");
}else{
callback("SUCCESS got streamers " + result);
}
});
}

using AJAX to call one query from a PHP file which has multiple

I would like to use AJAX to call a single query in a php file which has multiple but I'm not entirely sure how to go about doing this.
In essence the php file will have three PDO queries which will be update, delete and add. Currently it just has the update query.
How would i add a second query to the file and get AJAX to call particular query?
The AJAX code:
function updateCall() {
var data = $('#updateForm').serialize();
$.post('ManageCourses_DeleteSubmit.php', data, function(response){
$("#updateForm").html(response);
//'soft'reload parent page, after a delay to show message
setTimeout(function(){
$('#editModal').modal('hide')
location.reload();
},2000);
}).fail(function(jqXHR, textStatus) {
alert( "Request failed: " + textStatus );
});
}
the php file:
<?php
include "db_conx.php";
try
{
$db_conx = new PDO("mysql:host=$mysql_hostname;dbname=$mysql_dbname", $mysql_username, $mysql_password);
$db_conx->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = $db_conx->prepare("UPDATE course_details SET course_title = :course_title
WHERE course_code = :course_code");
$course_title = $_POST['course_title'];
$course_code = $_POST['course_code'];
$sql->bindParam(':course_title', $course_title, PDO::PARAM_STR);
$sql->bindParam(':course_code', $course_code, PDO::PARAM_STR);
/*** execute the prepared statement ***/
$sql->execute();
/*** success message ***/
$message = "<p class='text-success'> Record Successfully Updated <span class='glyphicon glyphicon-ok'/></p>";
}
catch(Exception $e)
{
$message = 'Message: ' .$e->getMessage();
}
die($message);
?>
Any examples?
Thanks!
The $.ajax() code block looks like this:
$.ajax({
type: "POST",
url: "receiving_file.php",
data: 'selected_opt=' + opt + '&something_else=' +someelse+'&more_stuff='+more_stuff,
success:function(data){
alert('This was sent back: ' + data);
}
});
Note the data: line
Just use another variable to identify which PHP routine you will call. Something like:
data: 'myrequest=add&selected_opt=' + opt + '&something_else=' +someelse+'&more_stuff='+more_stuff,
versus
data: 'myrequest=delete&selected_opt=' + opt + '&something_else=' +someelse+'&more_stuff='+more_stuff,
Then, in your PHP file, test for that variable:
<?php
$req = $_POST['myrequest'];
if ($req == 'add'){
//do the add
}else if ($req == 'delete'){
//etc
}
Alternately, you can use a single $.ajax() code block, and use a variable to determine which PHP function to call:
if (useraction=='add') {
myreq = 'add';
}else if(useraction=='del') {
myreq = 'delete';
}
//later on in the code...
$.ajax({
type: "POST",
url: "receiving_file.php",
data: 'myrequest=' +myreq+ '&selected_opt=' + opt + '&something_else=' +someelse+'&more_stuff='+more_stuff,
success:function(data){
alert('This was sent back: ' + data);
}
});

JSON request time out or inconsistent result

I am using JSON and YQL to access some data from an external domain.
http://jsfiddle.net/NdkPU/22/
But the problem I am having is that the code only works intermittently, sometimes I get a response, sometimes nothing is returned.
Obviously this is a very slow solution to access this data - so one reason I thought might be that it is timing out or there is a problem with the callback function.
Any ideas why the result is not working all the time?
// Start function when DOM has completely loaded
$(document).ready(function(){
requestCrossDomain("http://api.fool.com/caps/ws/Ticker/GOOG/Pitches/10?apikey=rW3550aXdsuJPg4bKEemC13x39jDNR6f", function (result) {
var num = 1;
var browserName = navigator.appName;
var doc;
if (browserName == 'Microsoft Internet Explorer') {
doc = new ActiveXObject('Microsoft.XMLDOM');
doc.async = 'false'
doc.loadXML(result.results);
} else {
doc = (new DOMParser()).parseFromString(result.results, 'text/xml');
}
var xml = doc;
console.log("Data Loaded: " + xml);
// Build an HTML string
myHTMLOutput = '';
myHTMLOutput += '<table width="98%" border="1" cellpadding="0" cellspacing="0">';
myHTMLOutput += '<th>Text</th>';
// Run the function for each student tag in the XML file
$('Pitch',xml).each(function(i) {
PitchText = $(this).find("Text").text();
// Build row HTML data and store in string
mydata = BuildStudentHTML(PitchText);
myHTMLOutput = myHTMLOutput + mydata;
});
myHTMLOutput += '</table>';
myHTMLOutput += '<br>Rating: ';
myHTMLOutput += $('Ticker',xml).attr("Percentile");
// Update the DIV called Content Area with the HTML string
$("#ContentArea").append(myHTMLOutput);
});
});
function BuildStudentHTML(PitchText){
// Build HTML string and return
output = '';
output += '<tr>';
output += '<td>'+ PitchText + '</td>';
output += '</tr>';
return output;
}
// Accepts a url and a callback function to run.
function requestCrossDomain(site, callback) {
// If no url was passed, exit.
if (!site) {
alert('No site was passed.');
return false;
}
// Take the provided url, and add it to a YQL query. Make sure you encode it!
var yql = 'http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from xml where url="' + site + '"') + '&format=xml&callback=?';
// Request that YSQL string, and run a callback function.
// Pass a defined function to prevent cache-busting.
$.getJSON(yql, cbFunc);
function cbFunc(data) {
// If we have something to work with...
if (data.results[0]) {
if (typeof callback === 'function') {
callback(data);
}
}
// Else, Maybe we requested a site that doesn't exist, and nothing returned.
else throw new Error('Nothing returned from getJSON.');
}
}
It looks like it's just a slow API. I wrote a simple bash loop that timed that query using curl, and here were the timings:
7.4 sec
11.1 sec
11.2 sec
7.4 sec
11.1 sec
7.3 sec
10.2 sec
11.8 sec
11.3 sec
7.1 sec