I created (I copied) a chat server using Node.JS and the server is on my LocalHost: 127.0.0.1, only I can use the chat, but I want that anyone can use the server too, so I want to know how to put this Chat server in my real server:
http://sistema.agrosys.com.br/sistema/padrao/HTML5/WebSocket/
And : http://calnetaskmanager.herokuapp.com/
But I don't now how to put my chat-server.js into my Heroku Server.
What should I do to make it possible.
Thanks in advance
If you want to see what happens: client side on my server
Client Side:
$(function() {
"use strict";
// for better performance - to avoid searching in DOM
var content = $('#content');
var input = $('#input');
var status = $('#status');
// my color assigned by the server
var myColor = false;
// my name sent to the server
var myName = false;
// if user is running mozilla then use it's built-in WebSocket
window.WebSocket = window.WebSocket || window.MozWebSocket;
// if browser doesn't support WebSocket, just show some notification and exit
if (!window.WebSocket) {
content.html($('<p>', {
text: 'Sorry, but your browser doesn\'t ' + 'support WebSockets.'
}));
input.hide();
$('span').hide();
return;
}
// open connection
var connection = new WebSocket('ws://127.0.0.1:1337');
connection.onopen = function() {
// first we want users to enter their names
input.removeAttr('disabled');
status.text('Choose name:');
};
connection.onerror = function(error) {
// just in there were some problems with conenction...
content.html($('<p>', {
text: 'Sorry, but there\'s some problem with your ' + 'connection or the server is down.'
}));
};
// most important part - incoming messages
connection.onmessage = function(message) {
// try to parse JSON message. Because we know that the server always returns
// JSON this should work without any problem but we should make sure that
// the massage is not chunked or otherwise damaged.
try {
var json = JSON.parse(message.data);
} catch (e) {
console.log('This doesn\'t look like a valid JSON: ', message.data);
return;
}
// NOTE: if you're not sure about the JSON structure
// check the server source code above
if (json.type === 'color') { // first response from the server with user's color
myColor = json.data;
status.text(myName + ': ').css('color', myColor);
input.removeAttr('disabled').focus();
// from now user can start sending messages
} else if (json.type === 'history') { // entire message history
// insert every single message to the chat window
for (var i = 0; i < json.data.length; i++) {
addMessage(json.data[i].author, json.data[i].text,
json.data[i].color, new Date(json.data[i].time));
}
} else if (json.type === 'message') { // it's a single message
input.removeAttr('disabled'); // let the user write another message
addMessage(json.data.author, json.data.text,
json.data.color, new Date(json.data.time));
} else {
console.log('Hmm..., I\'ve never seen JSON like this: ', json);
}
};
/**
* Send mesage when user presses Enter key
*/
input.keydown(function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
if (!msg) {
return;
}
// send the message as an ordinary text
connection.send(msg);
$(this).val('');
// disable the input field to make the user wait until server
// sends back response
input.attr('disabled', 'disabled');
// we know that the first message sent from a user their name
if (myName === false) {
myName = msg;
}
}
});
/**
* This method is optional. If the server wasn't able to respond to the
* in 3 seconds then show some error message to notify the user that
* something is wrong.
*/
setInterval(function() {
if (connection.readyState !== 1) {
status.text('Error');
input.attr('disabled', 'disabled').val('Unable to comminucate ' + 'with the WebSocket server.');
}
}, 3000);
/**
* Add message to the chat window
*/
function addMessage(author, message, color, dt) {
content.prepend('<p><span style="color:' + color + '">' + author + '</span> # ' +
+(dt.getHours() < 10 ? '0' + dt.getHours() : dt.getHours()) + ':' + (dt.getMinutes() < 10 ? '0' + dt.getMinutes() : dt.getMinutes()) + ': ' + message + '</p>');
}
});
* {
font-family: tahoma;
font-size: 12px;
padding: 0px;
margin: 0px;
}
p {
line-height: 18px;
}
div {
width: 500px;
margin-left: auto;
margin-right: auto;
}
#content {
padding: 5px;
background: #ddd;
border-radius: 5px;
overflow-y: scroll;
border: 1px solid #CCC;
margin-top: 10px;
height: 160px;
}
#input {
border-radius: 2px;
border: 1px solid #ccc;
margin-top: 10px;
padding: 5px;
width: 400px;
}
#status {
width: 88px;
display: block;
float: left;
margin-top: 15px;
}
<!DOCTYPE html>
<html>
<head>
<style type="text/css"></style>
<meta charset="utf-8">
<title>WebSockets - Simple chat</title>
</head>
<body>
<div id="content"></div>
<div>
<span id="status">Choose name:</span>
<input type="text" id="input">
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="./frontend.js"></script>
</body>
Server Side
// http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
"use strict";
// Optional. You will see this name in eg. 'ps' or 'top' command
process.title = 'node-chat';
// Port where we'll run the websocket server
var webSocketsServerPort = 1337;
// websocket and http servers
var webSocketServer = require('websocket').server;
var http = require('http');
/**
* Global variables
*/
// latest 100 messages
var history = [];
// list of currently connected clients (users)
var clients = [];
/**
* Helper function for escaping input strings
*/
function htmlEntities(str) {
return String(str).replace(/&/g, '&').replace(/</g, '<')
.replace(/>/g, '>').replace(/"/g, '"');
}
// Array with some colors
var colors = ['red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange'];
// ... in random order
colors.sort(function(a, b) {
return Math.random() > 0.5;
});
/**
* HTTP server
*/
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
});
/**
* WebSocket server
*/
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server. WebSocket request is just
// an enhanced HTTP request. For more info http://tools.ietf.org/html/rfc6455#page-6
httpServer: server
});
// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
// accept connection - you should check 'request.origin' to make sure that
// client is connecting from your website
// (http://en.wikipedia.org/wiki/Same_origin_policy)
var connection = request.accept(null, request.origin);
// we need to know client index to remove them on 'close' event
var index = clients.push(connection) - 1;
var userName = false;
var userColor = false;
console.log((new Date()) + ' Connection accepted.');
// send back chat history
if (history.length > 0) {
connection.sendUTF(JSON.stringify({
type: 'history',
data: history
}));
}
// user sent some message
connection.on('message', function(message) {
if (message.type === 'utf8') { // accept only text
if (userName === false) { // first message sent by user is their name
// remember user name
userName = htmlEntities(message.utf8Data);
// get random color and send it back to the user
userColor = colors.shift();
connection.sendUTF(JSON.stringify({
type: 'color',
data: userColor
}));
console.log((new Date()) + ' User is known as: ' + userName + ' with ' + userColor + ' color.');
} else { // log and broadcast the message
console.log((new Date()) + ' Received Message from ' + userName + ': ' + message.utf8Data);
// we want to keep history of all sent messages
var obj = {
time: (new Date()).getTime(),
text: htmlEntities(message.utf8Data),
author: userName,
color: userColor
};
history.push(obj);
history = history.slice(-100);
// broadcast message to all connected clients
var json = JSON.stringify({
type: 'message',
data: obj
});
for (var i = 0; i < clients.length; i++) {
clients[i].sendUTF(json);
}
}
}
});
// user disconnected
connection.on('close', function(connection) {
if (userName !== false && userColor !== false) {
console.log((new Date()) + " Peer " + connection.remoteAddress + " disconnected.");
// remove user from the list of connected clients
clients.splice(index, 1);
// push back user's color to be reused by another user
colors.push(userColor);
}
});
});
you need to host your Node.js code somewhere, for example on Heroku, they have free plans for Node.js, you will then be given a public url, which you can distribute to others
Make it available to everyone... This is easy.
Deploy your code to a publicly available host -- say Heroku as
mentioned by #dark_ruby in his answer.
Provide a computing device with a browser (a nexus 7 will do) to
every person in the entire world
Configure it to have your service as the default home page
Done.
Related
What I have:
Node.js script running Blessed, and http/websocket server.
Browser running Xterm.js and websocket client.
What I want to do:
Render blessed to the xterm window over websockets.
Server Code:
"use strict";
process.title = 'neosim-server';
var blessed = require('neo-blessed');
var contrib = require('blessed-contrib');
var webSocketServer = require('websocket').server;
var http = require('http');
const webSocketsServerPort = 8080;
var clients = [ ];
/**
* HTTP server
*/
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server,
// not HTTP server
});
server.listen(webSocketsServerPort, function() {
console.log((new Date()) + " Server is listening on port "
+ webSocketsServerPort + ".");
});
/**
* WebSocket server
*/
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server. WebSocket
// request is just an enhanced HTTP request. For more info
// http://tools.ietf.org/html/rfc6455#page-6
httpServer: server,
autoAcceptConnections: false
});
function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}
// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}
console.log((new Date()) + ' Connection from origin '
+ request.origin + '.');
// accept connection - you should check 'request.origin' to
// make sure that client is connecting from your website
// (http://en.wikipedia.org/wiki/Same_origin_policy)
var connection = request.accept(null, request.origin);
// we need to know client index to remove them on 'close' event
connection.write = connection.send;
connection.read = connection.socket.read;
connection.program = blessed.program({
tput: true,
input: connection,
output: connection
});
connection.program.tput = blessed.tput({
term: 'xterm-256color',
extended: true,
});
connection.screen = blessed.screen({
program: connection.program,
tput: connection.program.tput,
input: connection,
output: connection,
//smartCSR: true,
terminal: 'xterm-256color',
fullUnicode: true
});
var index = clients.push(connection) - 1;
var userName = false;
console.log((new Date()) + ' Connection accepted.');
connection.program.write("test");
// send back chat history
/*if (history.length > 0) {
connection.sendUTF(
JSON.stringify({ type: 'history', data: history} ));
}*/
// terminal type message
connection.on('term', function(terminal) {
console.log("Term");
connection.screen.terminal = terminal;
connection.screen.render();
});
connection.on('resize', function(width, height) {
console.log("Resize");
connection.columns = width;
connection.rows = height;
connection.emit('resize');
});
// user sent some message
connection.on('message', function(message) {
if (message.type === 'utf8') { // accept only text
console.log((new Date()) + ' Received Message: ' + message.utf8Data);
}
});
// user disconnected
connection.on('close', function(connection) {
if (connection.screen && !connection.screen.destroyed) {
connection.screen.destroy();
}
});
connection.screen.key(['C-c', 'q'], function(ch, key) {
connection.screen.destroy();
});
connection.screen.on('destroy', function() {
if (connection.writable) {
connection.destroy();
}
});
connection.screen.data.main = blessed.box({
parent: connection.screen,
left: 'center',
top: 'center',
width: '80%',
height: '90%',
border: 'line',
content: 'Welcome to my server. Here is your own private session.'
});
connection.screen.render();
});
Client page:
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="node_modules/xterm/dist/xterm.css" />
<script src="node_modules/xterm/dist/xterm.js"></script>
<script src="node_modules/xterm/dist/addons/attach/attach.js"></script>
<script src="node_modules/xterm/dist/addons/fit/fit.js"></script>
<script src="node_modules/xterm/dist/addons/winptyCompat/winptyCompat.js"></script>
</head>
<body>
<div id="terminal"></div>
<script>
Terminal.applyAddon(attach);
Terminal.applyAddon(fit);
Terminal.applyAddon(winptyCompat);
var term = new Terminal();
var socket = new WebSocket('ws://localhost:8080', null);
term.open(document.getElementById('terminal'));
term.winptyCompatInit();
term.fit();
term.focus();
term.write('Terminal ('+term.cols+'x'+term.rows+')\n\r');
// term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
// window.addEventListener('resize', resizeScreen, false)
function resizeScreen () {
term.fit();
socket.emit('resize', { cols: term.cols, rows: term.rows });
};
socket.onopen = function (connection) {
term.attach(socket, true, false);
//term.emit('term');
//term.emit('resize');
};
socket.onmessage = function (message) {
term.write(message);
};
</script>
</body>
</html>
The issue I am having is when screen.render() is called, nothing shows up on the client. When creating my blessed.screen, I've attempted using:
connection.screen = blessed.screen({input: connection.socket, output: connection.socket});
But this does not work either. In my current server code, I was attempting to trick blessed into using connection.send rather than connect.socket.write, because I think that browser websockets only accept the 'onmessage' event. Like so:
connection.write = connection.send;
connection.read = connection.socket.read;
connection.screen = blessed.screen({input: connection, output: connection});
Nothing I have tried so far has worked. What am I doing wrong here? Or is it simply that blessed won't work with browser websockets. Also, I know the connection is working, because I can send/receive messages on both ends.
I got it working. I added the http responses to your server for serving the html/js files - just to get everything in one place. The main problem was the second parameter to your websocket connection in your client which is null. I just removed it, and then it was playing.
Also added an echo of your typing when pressing CR, so that you'll get a response from the server.
See modified sources below
Server code:
process.title = 'neosim-server';
var blessed = require('neo-blessed');
var contrib = require('blessed-contrib');
var webSocketServer = require('websocket').server;
var http = require('http');
var fs = require('fs');
const webSocketsServerPort = 8080;
var clients = [ ];
/**
* HTTP server
*/
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server,
// not HTTP server
let path = request.url.substring(1);
if(path === '') {
path = 'index.html';
}
if(fs.existsSync(path)) {
if(path.indexOf('.js') === path.length-3) {
response.setHeader('Content-Type', 'application/javascript');
}
response.end(fs.readFileSync(path));
} else {
response.statusCode = 404;
response.end('');
}
});
server.listen(webSocketsServerPort, function() {
console.log((new Date()) + " Server is listening on port "
+ webSocketsServerPort + ".");
});
/**
* WebSocket server
*/
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server. WebSocket
// request is just an enhanced HTTP request. For more info
// http://tools.ietf.org/html/rfc6455#page-6
httpServer: server,
autoAcceptConnections: false
});
function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}
// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}
console.log((new Date()) + ' Connection from origin '
+ request.origin + '.');
// accept connection - you should check 'request.origin' to
// make sure that client is connecting from your website
// (http://en.wikipedia.org/wiki/Same_origin_policy)
var connection = request.accept(null, request.origin);
// we need to know client index to remove them on 'close' event
connection.write = connection.send;
connection.read = connection.socket.read;
connection.program = blessed.program({
tput: true,
input: connection,
output: connection
});
connection.program.tput = blessed.tput({
term: 'xterm-256color',
extended: true,
});
connection.screen = blessed.screen({
program: connection.program,
tput: connection.program.tput,
input: connection,
output: connection,
//smartCSR: true,
terminal: 'xterm-256color',
fullUnicode: true
});
var index = clients.push(connection) - 1;
var userName = false;
console.log((new Date()) + ' Connection accepted.');
connection.program.write("test");
// send back chat history
/*if (history.length > 0) {
connection.sendUTF(
JSON.stringify({ type: 'history', data: history} ));
}*/
// terminal type message
connection.on('term', function(terminal) {
console.log("Term");
connection.screen.terminal = terminal;
connection.screen.render();
});
connection.on('resize', function(width, height) {
console.log("Resize");
connection.columns = width;
connection.rows = height;
connection.emit('resize');
});
let buf = '';
// user sent some message
connection.on('message', function(message) {
if (message.type === 'utf8') { // accept only text
console.log((new Date()) + ' Received Message: ' + message.utf8Data);
buf += message.utf8Data;
if(message.utf8Data === '\r') {
console.log('You wrote:', buf);
connection.sendUTF(buf +'\r\n');
buf = '';
}
}
});
// user disconnected
connection.on('close', function(connection) {
if (connection.screen && !connection.screen.destroyed) {
connection.screen.destroy();
}
});
connection.screen.key(['C-c', 'q'], function(ch, key) {
connection.screen.destroy();
});
connection.screen.on('destroy', function() {
if (connection.writable) {
connection.destroy();
}
});
connection.screen.data.main = blessed.box({
parent: connection.screen,
left: 'center',
top: 'center',
width: '80%',
height: '90%',
border: 'line',
content: 'Welcome to my server. Here is your own private session.'
});
connection.screen.render();
});
client code:
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="node_modules/xterm/dist/xterm.css" />
<script src="node_modules/xterm/dist/xterm.js"></script>
<script src="node_modules/xterm/dist/addons/attach/attach.js"></script>
<script src="node_modules/xterm/dist/addons/fit/fit.js"></script>
<script src="node_modules/xterm/dist/addons/winptyCompat/winptyCompat.js"></script>
</head>
<body>
<div id="terminal"></div>
<script>
Terminal.applyAddon(attach);
Terminal.applyAddon(fit);
Terminal.applyAddon(winptyCompat);
var term = new Terminal();
var socket = new WebSocket('ws://localhost:8080');
term.open(document.getElementById('terminal'));
term.winptyCompatInit();
term.fit();
term.focus();
term.write('Terminal ('+term.cols+'x'+term.rows+')\n\r');
// term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
// window.addEventListener('resize', resizeScreen, false)
function resizeScreen () {
term.fit();
socket.emit('resize', { cols: term.cols, rows: term.rows });
};
socket.onopen = function (connection) {
console.log('connection opened');
term.attach(socket, true, false);
//term.emit('term');
//term.emit('resize');
};
socket.onmessage = function (message) {
term.write(message);
};
</script>
</body>
</html>
Im currently switching my application from using Socket.io to HTML5 WebSockets. I'm assuming that my problem lies within the first couple lines of both the client and server. My browser keeps on replying with a 426 (Upgrade Required) status when I test my app on localhost. Please shed some light on my problem...
Server Code
"use strict";
var session = require('./chat-session'),
serveStatic = require('serve-static'),
server = require('http').createServer(),
WebSocketServer = require('ws').Server,
wss = new WebSocketServer({server: server, port: 8181}),
express = require('express'),
app = express();
// Sockets with real-time data
// io = require('socket.io').listen(server),
// mongoose = require('mongoose');
app.use(express.static(__dirname + '/public')); // used for external files on client
let storage = session.default; // cache object for storage
// Routing refers to determining how an application responds to a client request to a particular endpoint
app.get('/', function(req, res){
res.sendFile(__dirname + '/public/index.html');
});
wss.on('connection', function(client){
client.on('join', (name) => {
client.nickname = name;
// check to see if nickname has been taken, if so, give random name
if(storage.users.indexOf(client.nickname) !== -1) {client.nickname = randomName();}
// tell all chatters that a new user has entered the room
client.broadcast.emit("enter", "* " + client.nickname + " * has connected");
storage.users.forEach((user) => {
client.emit('add chatter', user);
});
client.broadcast.emit('add chatter', client.nickname);
storage.channels.general.messages.forEach((message) => {
client.emit("message", message.name + ": " + message.data, 'general');
});
storage.users.push(client.nickname);
});
client.on('message', (message, room) => {
var nickname = client.nickname;
client.broadcast.emit("message", nickname + ": " + message, room);
client.emit("me", message); // send message to self
storeMessage(nickname, message, room); // store the message in chat-session
console.log(nickname + ' said: ' + message + " in room " + room);
});
// When client switches between tabs (rooms)
client.on('switch', (room) => {
storage.channels[room].messages.forEach((message) => {
if (message.name === client.nickname) {
client.emit("me", message.data, room);
} else {
client.emit("message", message.name + ": " + message.data, room);
}
});
});
// client.on('disconnect', () => {
// client.emit('disconnect', "client")
// });
});
//const PORT = 8080;
//server.listen(PORT);
Client Code
// var server = io.connect("http://localhost:8080"); // connect to server
var server = new WebSocketServer("ws://localhost:8181");
var curRoom = $('.nav .active').attr('id'); // cache current room
server.on('connect', function(data){
nickname = prompt("What is your nickname?");
//while(nickname) TODO:make sure client cannot choose null
server.emit('join', nickname); // notify the server of the users nickname
});
//server.on('disconnect', function(data){
// server.emit('disconnect');
//});
// new chatter enters room
server.on('enter', function(data){
$('#messages').append($('<li style="background:#33cc33; color:white">').text(data));
});
// connected users section
server.on('add chatter', function(name){
var chatter = $('<li style="color:white; font-size:22px">' + name + '</li>').data('name', name);
$('#users').append(chatter);
});
// users' send message
server.on('message', function(message, room){
// only emit message to other users if they are in same channel
if (curRoom === room) {
$('#messages').append($('<li style="display:table; box-shadow: 6px 3px 8px grey;">').text(message));
play(); // invoke function to play sound to other clients
console.log('sound played here');
}
});
// differentiate how the client sees their message
server.on('me', function(message){
$('#messages').append($('<li style="background:#0066ff; color:white; display:table; box-shadow: 6px 3px 8px grey;">').text(message));
});
// Client submits message
$('#chat_form').submit(function(e){
var message = $("#chat_input").val();
server.emit('message', message, curRoom);
$('#chat_input').val(''); // Make input box blank for new message
return false; // prevents refresh of page after submit
});
Http 426 means that you are trying to connect unsupported web-socket version .
You can check in the client headers for supported version .
Refer to RFC for more detail
https://www.rfc-editor.org/rfc/rfc6455#section-4.2.2
I have a web application that performs a Jquery .post request for database queries. I also have three different web socket connections that are used to push status updates from the server to the client (CPU and Memory stats in a live chart, database status, and query queue). While a query is not running, everything works smoothly, but once a query is started (post request), then the three web socket connections seem to hang/block while waiting for the query to return. I was reading about this and have not found any relevant answers...I suspect that it is probably something really dumb on my part...but this has had me scratching my head for the better part of a day now. I thought I might try moving the web socket connections to web workers...but in theory, the POST should not be blocking to begin with...So, here are the relevant snippets of code...The full source is a couple of thousand lines of code...so I didn't want to inundate anyone with it...but could show it if it is useful. So, the big question is what am I doing wrong here? Or perhaps, am I misunderstanding how AJAX calls work as far as blocking goes?
// query execution button that grabs the query for the most recently focused query source (SPARQL editor, history, or canned)
$("#querySubmitButton").on("click", function(e) {
// Disable the query button
$("#querySubmitButton").attr('disabled',true);
// Let's make sure we are clearing out the work area and the popup contents
$("#viz").empty();
// Get YASQE to tell us what type of query we are running
var queryType = editor.getQueryType();
// refactored so that we can clean up the on-click function and also make other query types in a more modular way
switch(queryType) {
case 'SELECT':
sparqlSelect();
break;
case 'CONSTRUCT':
sparqlConstruct();
break;
case 'ASK':
sparqlAsk();
break;
case 'DESCRIBE':
sparqlDescribe();
break;
case 'INSERT':
sparqlInsert();
break;
default:
popup.show("Unrecognized query type.","error");
break;
}
});
// Functions to do each of the query types (SELECT, CONSTRUCT, ASK, DESCRIBE, INSERT)
// SELECT
function sparqlSelect() {
$.post("sparqlSelect", { database: $("#DB_label").html(),'query': editor.getValue() }).done(function(data, textStatus, xhr) {
// Enable the query button
$("#querySubmitButton").removeAttr('disabled');
// If the query worked, store it
storeQueryHistory(query);
// if the previous query was a CONSTRUCT, then lets hide the graph metrics button
$("#nav-trigger-graphStatistics").fadeOut(800);
// Need to slide the query menu back
sliders("in",$("#nav-trigger-query").attr("id"));
var columns = [];
var fields = [];
var comboboxFields = [];
// Hide the graph search panel
$("#graphSearch").fadeOut(1400);
// Show the results and visualization button/tab
$("#nav-trigger-results").fadeIn(1400);
$("#nav-trigger-visualization").fadeIn(1400);
$.each(data.results.head.vars, function(index, value) {
columns.push({'field': value, 'title': value});
var to = {};
to[value] = {type: "string"};
fields.push(to);
// Let's also populate the two Comboboxes for the Visualization while we are at it
comboboxFields.push({'text': value, 'value': value});
});
// Now, set the two combobox datasources for visualizations
var categoriesDS = new kendo.data.DataSource({
data: comboboxFields
});
vizCategoryAxis.setDataSource(categoriesDS);
var valuesDS = new kendo.data.DataSource({
data: comboboxFields
});
vizValueAxis.setDataSource(valuesDS);
var dataBindings = [];
$.each(data.results.results.bindings, function(index1, value) {
var tempobj = {};
$.each(value, function(k1,v1) {
tempobj[k1] = v1.value;
});
tempobj.id=index1;
dataBindings.push(tempobj);
});
var configuration = {
dataSource: {
data: dataBindings,
pageSize: 25
},
height: 400,
scrollable: true,
sortable: true,
filterable: true,
reorderable: true,
resizable: true,
toolbar: ["excel"],
excel: {
allPages: true,
filterable: true,
proxyURL: "/saveExcel"
},
pageable: {
input: true,
numeric: false,
pageSizes: true
},
'columns': columns,
dataBound: function(e) {
$(e.sender.element).find('td').each(function() {
var temp = $(this).html();
if (isUrl(temp)) {
$(this).html('' + temp + '');
}
});
}
};
// Create the popup window
var gridWindow = $("#resultsPopup").kendoWindow({
width: "70%",
title: "Query Results",
actions: [
"Minimize",
"Maximize",
"Close"
]
}).data('kendoWindow');
// Center and show the popup window
gridWindow.center().open();
// Create/update/refresh the grid
resultsGrid.setOptions(configuration);
resultsGrid.dataSource.page(1);
$("#nav-trigger-results").on('click',function() {
// Center and show the popup window
gridWindow.center().open();
});
}).fail(function(xhr) {
// If we are timed-out
if (xhr.status === 401) {
// First, clear the host, database, and status text
$("#host_label").html('');
$("#DB_label").html('');
$("#status_label").html('');
// Next, disable the query button
$("#querySubmitButton").attr('disabled',true);
// Change "login" tab text color to red so we know we are no longer logged in
var styles = { 'color': "#FFCCD2" };
$("#nav-trigger-login").css(styles);
popup.show("Session for " + host + " has timed out, please log back in.","error");
}
else {
// Enable the query button
$("#querySubmitButton").removeAttr('disabled');
popup.show("Error, no results (" + xhr.status + " " + xhr.statusText + ")","error");
}
});
}
// Function to connect to the query queue websocket
function queueWebsocketConnect() {
var qws = new WebSocket('wss://endeavour:3000/queue');
// Let's disconnect our Websocket connections when we leave the app
$(window).on('unload', function() {
console.log('Websocket connection closed');
qws.close();
});
// Status websocket onopen
qws.onopen = function () {
console.log('Websocket connection opened');
popup.show("Websocket connection opened","success");
};
qws.onclose = function (event) {
console.log('Websocket connection closed');
popup.show("Websocket connection closed","info");
};
qws.onmessage = function (msg) {
var res = JSON.parse(msg.data);
var tableRows = '<thead><tr><td>Query Position</td><td>Query ID</td><td>Kill/Cancel Query</td></tr></thead><tbody>';
if (res.executing != null && res.entry.length > 0) {
$("#queryQueue").empty();
tableRows += '<tr><td>1</td><td>' + res.executing.id + '</td><td><input type="button" class="k-button" value="Kill"></td></tr>';
$.each(res.entry, function(index,object) {
tableRows += '<tr><td>' + (object.pos + 1) + '</td><td>' + object.query.id + '</td><td><input type="button" class="k-button" value="Cancel"></td></tr>';
});
tableRows += '</tbody>';
$("#queryQueue").html(tableRows);
}
else if (res.executing != null) {
$("#queryQueue").empty();
tableRows += '<tr><td>1</td><td>' + res.executing.id + '</td><td><input type="button" class="k-button" value="Kill"></td></tr>';
tableRows += '</tbody>';
$("#queryQueue").html(tableRows);
}
else {
console.log(res);
$("#queryQueue").empty();
}
};
}
// Function to connect to the stats websocket
function websocketConnect () {
// Set up websocket connection for system stats
var ws = new WebSocket('wss://endeavour:3000/stats');
// Let's disconnect our Websocket connections when we leave the app
$(window).on('unload', function() {
console.log('Websocket connection closed');
ws.close();
});
// Status websocket onopen
ws.onopen = function () {
console.log('Websocket connection opened');
popup.show("Websocket connection opened","success");
};
// Status websocket onclose
ws.onclose = function (event) {
// Disable the query button
$("#querySubmitButton").attr('disabled',true);
// Change "login" tab text color to red so we know we are no longer logged in
var styles = { 'color': "#FFCCD2" };
$("#nav-trigger-login").css(styles);
// Clear the host, database, and status text
$("#host_label").html('');
$("#DB_label").html('');
$("#status_label").html('');
console.log('Websocket connection closed');
popup.show("Websocket connection closed","error");
$("#websocketReconnectButtonYes").on('click', function() {
websocketConnect();
queueWebsocketConnect();
websocketReconnect.close();
});
$("#websocketReconnectButtonNo").on('click', function() {
websocketReconnect.close();
});
websocketReconnect.center().open();
};
// When updates are received, push them out to update the details
var logoutCount = 0;
ws.onmessage = function (msg) {
if (msg.data === 'loggedOut') {
// Ensure we only emit this one time instead of a stream of them
if (logoutCount == 0) {
// Disable the query button
$("#querySubmitButton").attr('disabled',true);
// Change "login" tab text color to red so we know we are no longer logged in
var styles = { 'color': "#FFCCD2" };
$("#nav-trigger-login").css(styles);
// Clear the host, database, and status text
$("#host_label").html('');
$("#DB_label").html('');
$("#status_label").html('');
console.log("Session for " + $("#host_label").html() + " has timed out, please log back in.");
popup.show("Session for " + $("#host_label").html() + " has timed out, please log back in.","error");
}
logoutCount = 1;
}
else {
logoutCount = 0;
var res = JSON.parse(msg.data);
var host = $("#host_label").html();
var pdatabase = $("#DB_label").html();
var pstatus = $("#status_label").html();
// Disable the query button unless the database is "CONNECTED"
if ($("#status_label").html() !== res.current.databaseStatus) {
if (res.current.databaseStatus !== "CONNECTED") {
$("#querySubmitButton").attr('disabled',true);
}
else {
$("#querySubmitButton").removeAttr('disabled');
}
if (res.current.databaseStatus == 'CONNECTED' || res.current.databaseStatus == 'STOPPED') {
$("#startDB").removeAttr('disabled');
}
else {
$("#startDB").attr('disabled',true);
}
}
// Maybe a more intelligent way to do this, but need to make sure that if the cookie is still valid, then populate the database login stuff
if ($("#dbConfigHost").val() == "" && $("#dbConfigUser").val() == "") {
$("#dbConfigHost").val(res.host);
$("#dbConfigUser").val(res.user);
// Change "login" tab text color to green so we know we are logged in
var styles = { 'color': "#C5E6CC" };
$("#nav-trigger-login").css(styles);
var databasesDS = new kendo.data.DataSource({
data: res.databases.database
});
databasePicker.setDataSource(databasesDS);
}
// Update the labels when values change
if (res.host != $("#host_label").html()) {
$("#host_label").html(res.host);
popup.show("Host changed to " + res.host,"info");
}
if (pdatabase != res.current.name) {
$("#DB_label").html(res.current.name);
popup.show("Database changed to " + res.current.name ,"info");
}
if (pstatus != res.current.databaseStatus) {
$("#status_label").html(res.current.databaseStatus);
}
// Update the sparklines
cpulog.options.series[0].data = res.system.cpu;
cpulog.refresh();
memlog.options.series[0].data = res.system.mem;
memlog.refresh();
}
};
// Open the websocket connection to listen for changes to the query list
var queryWS = new WebSocket('wss://endeavour:3000/queryList');
queryWS.onmessage = function(msg) {
var res = JSON.parse(msg.data);
var queriesDS = new kendo.data.DataSource({
data: res
});
cannedQuery.setDataSource(queriesDS);
};
}
Well, I guess when one has been heading down one road for a while, one assumes that it is in the right direction. After further head-scratching, I found the issue and it was related to the blocking/non-blocking nature of my backend web-framework. As it turns out, in Mojolicious (Perl web-framework), http calls can be either synchronous or asynchronous depending on how one writes the call.
my $tx = $ua->get('http://foo.bar?query=getSomeFoo');
if($tx->success) {
$self->render($tx->res->content);
}
else {
$self->rendered($tx->res->code);
}
This is a blocking/synchronous request. Nothing happens until after the GET finishes. On the other hand, if one writes the request like so, it is an asynchronous request:
$ua->get('http://foo.bar?query=getSomeFoo' => sub {
my ($ua,$tx) = #_;
if($tx->success) {
$self->render($tx->res->content);
}
else {
$self->rendered($tx->res->code);
}
});
So, if anyone else has encountered this issue...here is the answer. If I am the only idiot on the planet that has committed this blunder...then I guess I have put my shame out there for all to have a good chuckle at.
Bottom line was that this was well documented in the Mojolicious docs...but I had been doing it one way for so long that I completely forgot about it.
Cheers!
I am trying to create realtime chat in my SPA app. I got the chat working , but when I move to another page and head back to the chat page , it starts loading messages twice. Here is my server side for socket.io:
var io = require('socket.io').listen(app.listen(config.port));
io.sockets.on('connection', function (socket) {
socket.emit('message', { message: 'You are chatting now !' });
socket.on('send', function (data) {
io.sockets.emit('message', data);
});
I tried using io.sockets.once instead of io.sockets.on but it doesn't show chat 2nd time ..
And here is my client side together with html (jade template engine):
#content(style='width: 500px; height: 300px; margin: 0 0 20px 0; border: solid 1px #999; overflow-y: scroll;')
input#field(style='width:350px;')
input#send(type='button', value='send')
script(src='/socket.io/socket.io.js')
script.
$("#field").keyup(function(e) {
if(e.keyCode == 13) {
sendMessage();
}
});
var messages = [];
var socket = io.connect('localhost:3030/#/chat');
var field = document.getElementById("field");
var sendButton = document.getElementById("send");
var content = document.getElementById("content");
socket.on('message', function (data) {
if(data.message) {
messages.push(data.message);
var html = '';
for(var i=0; i<messages.length; i++) {
html += messages[i] + '<br />';
}
content.innerHTML = html;
content.scrollTop = content.scrollHeight;
} else {
console.log("There is a problem:", data);
}
});
sendButton.onclick = sendMessage = function() {
var text = field.value;
socket.emit('send', { message: text });
field.value = '';
};
I want to fix this , so when I go back 2nd time it doesnt show 2 times , or for 3rd time to show it 3 times and so on ... Help is greatly appreciated :)
If I understand correctly, you want correct persistence between page refreshes, just add an array on the server to collect the messages and tell new connections all about them.
I really just use the docs to accomplish my goals (link for reference http://socket.io/docs/server-api/)
for the code below to run, it is necessary to do these commands in the working directory
npm install express
npm install socket.io
(server code)
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
//serve up the file, the code for this is below, I translated your jade to html
app.get('/', function(req, res){
res.sendfile('index.html');
});
//note this will get big eventually with lots of users
var message_past = [];
io.on('connection', function(socket){
socket.emit('server-message', { message: 'Welcome !' });
// tell user recent posts
for (var i = 0; i < 20 && !!message_past[i] ; i ++) socket.emit('user-message',message_past[i]);
// save the message and update everyone with it
socket.on('user-message', function (data) {
message_past.push(data);
// io.emit will tell everyone <-- I think what you want
// socket.broadcast.emit will tell everyone but this socket
// socket.emit will tell just this socket
io.emit('user-message', data);
});
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
and the client code
<input id='field' type="text" style='width:350px;'/>
<input id='send' type='button' value='send'/>
<div id='content' style='width: 500px; height: 300px; margin: 0 0 20px 0; border: solid 1px #999; overflow-y: scroll;'>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src='/socket.io/socket.io.js' ></script>
<script type="text/javascript">
$("#field").keyup(function(e) {
if(e.keyCode == 13) {
sendMessage();
}
});
var socket = io.connect('localhost:3000/#/chat');
console.log(socket)
var field = document.getElementById("field");
var sendButton = document.getElementById("send");
var content = document.getElementById("content");
var display_message = function( data ) {
if(data.message) {
$(content).append(data.message+'<br/>');
content.scrollTop = content.scrollHeight;
} else {
console.log("There is a problem:", data);
}
};
socket.on('server-message', display_message);
socket.on('user-message', display_message);
sendButton.onclick = sendMessage = function() {
var text = field.value;
socket.emit('user-message', { message: text });
field.value = '';
};
</script>
I have been developing a nodejs server to provide server-side-events for a new website I am developing in HTML5.
When I telnet to the server it works correctly, sending me the required HTTP response headers followed by a stream of events that i am presently generating every 2 or 3 seconds just to prove it works.
I have tried the latest version of FireFox, Chrome and Opera and they create the EventSource object and connect to the nodejs server OK but none of the browsers generate any of the events, including the onopen, onmessage and onerror.
However, if I stop my nodejs server, terminating the connection from the browsers, they all suddenly dispatch all the messages and all my events are shown. The browsers then all try to reconnect to the server as per spec.
I am hosting everything on a webserver. nothing is running in local files.
I have read everything I can find online, including books I've purchased and nothing indicates any such problem. Is there something Im missing?
A sample server implementation
var http = require('http');
var requests = [];
var server = http.Server(function(req, res) {
var clientIP = req.socket.remoteAddress;
var clientPort = req.socket.remotePort;
res.on('close', function() {
console.log("client " + clientIP + ":" + clientPort + " died");
for(var i=requests.length -1; i>=0; i--) {
if ((requests[i].ip == clientIP) && (requests[i].port == clientPort)) {
requests.splice(i, 1);
}
}
});
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'});
requests.push({ip:clientIP, port:clientPort, res:res});
res.write(": connected.\n\n");
});
server.listen(8080);
setInterval(function test() {
broadcast('poll', "test message");
}, 2000);
function broadcast(rtype, msg) {
var lines = msg.split("\n");
for(var i=requests.length -1; i>=0; i--) {
requests[i].res.write("event: " + rtype + "\n");
for(var j=0; j<lines.length; j++) {
if (lines[j]) {
requests[i].res.write("data: " + lines[j] + "\n");
}
}
requests[i].res.write("\n");
}
}
A sample html page
<!DOCTYPE html>
<html>
<head>
<title>SSE Test</title>
<meta charset="utf-8" />
<script language="JavaScript">
function init() {
if(typeof(EventSource)!=="undefined") {
var log = document.getElementById('log');
if (log) {
log.innerHTML = "EventSource() testing begins..<br>";
}
var svrEvents = new EventSource('/sse');
svrEvents.onopen = function() {
connectionOpen(true);
}
svrEvents.onerror = function() {
connectionOpen(false);
}
svrEvents.addEventListener('poll', displayPoll, false); // display multi choice and send back answer
svrEvents.onmessage = function(event) {
var log = document.getElementById('log');
if (log) {
log.innerHTML += 'message: ' + event.data + "<br>";
}
// absorb any other messages
}
} else {
var log = document.getElementById('log');
if (log) {
log.innerHTML = "EventSource() not supported<br>";
}
}
}
function connectionOpen(status) {
var log = document.getElementById('log');
if (log) {
log.innerHTML += 'connected: ' + status + "<br>";
}
}
function displayPoll(event) {
var html = event.data;
var log = document.getElementById('log');
if (log) {
log.innerHTML += 'poll: ' + html + "<br>";
}
}
</script>
</head>
<body onLoad="init()">
<div id="log">testing...</div>
</body>
</html>
These examples are basic but of the same variety as every other demo i've seen in books and online. The eventSource only seems to be working if I end a client connection or terminate the server but this would be polling instead of SSE and I particularly want to use SSE.
Interestingly, demos, such as thouse from html5rock also seem to not quite work as expected when I use them online..
cracked it! :)
Thanks to some help from Tom Kersten who helped me with testing. Turns out the code isnt the problem.
Be warned.. if your client uses any kind of anti-virus software which intercepts web requests, it may cause problems here. In this case, Sophos Endpoint Security, which provides enterprise grade anti-virus and firewall protection has a feature called web protection. Within this features is an option to scan downloads; it seems that the SSE connection is treated as a download and thus not released to the browser until the connection is closed and the stream received to scan. Disabling this option cures the problem. I have submitted a bug report but other anti-virus systems may do the same.
thanks for your suggestions and help everyone :)
http://www.w3.org/TR/eventsource/#parsing-an-event-stream
Since connections established to remote servers for such resources are
expected to be long-lived, UAs should ensure that appropriate
buffering is used. In particular, while line buffering with lines are
defined to end with a single U+000A LINE FEED (LF) character is safe,
block buffering or line buffering with different expected line endings
can cause delays in event dispatch.
Try to play with line endings ("\r\n" instead of "\n").
http://www.w3.org/TR/eventsource/#notes
Authors are also cautioned that HTTP chunking can have unexpected
negative effects on the reliability of this protocol. Where possible,
chunking should be disabled for serving event streams unless the rate
of messages is high enough for this not to matter.
I modified your server-side script, which 'seems' partly works for Chrome.
But the connection break for every 2 broadcast & only 1 can be shown on client.
Firefox works for 1st broadcast and stop by this error:
Error: The connection to /sse was interrupted while the page was loading.
And Chrome will try to reconnect and received 3rd broadcast.
I think it's related to firewall setting too but can't explain why sometime will works.
Note: For event listener of response (line 10), 'close' & 'end' have different result,
You can try it and my result is [close: 1 success/2 broadcast] & [end: 1 success/8 broadcast]
var http = require('http'), fs = require('fs'), requests = [];
var server = http.Server(function(req, res) {
var clientIP = req.socket.remoteAddress;
var clientPort = req.socket.remotePort;
if (req.url == '/sse') {
var allClient="";for(var i=0;i<requests.length;i++){allClient+=requests[i].ip+":"+requests[i].port+";";}
if(allClient.indexOf(clientIP+":"+clientPort)<0){
requests.push({ip:clientIP, port:clientPort, res:res});
res.on('close', function() {
console.log("client " + clientIP + ":" + clientPort + " died");
for(var i=requests.length -1; i>=0; i--) {
if ((requests[i].ip == clientIP) && (requests[i].port == clientPort)) {
requests.splice(i, 1);
}
}
});
}
}else{
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(fs.readFileSync('./test.html'));
res.end();
}
});
server.listen(80);
setInterval(function test() {
broadcast('poll', "test message");
}, 500);
var broadcastCount=0;
function broadcast(rtype, msg) {
if(!requests.length)return;
broadcastCount++;
var lines = msg.split("\n");
for(var i = requests.length - 1; i >= 0; i--) {
requests[i].res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
requests[i].res.write("event: " + rtype + "\n");
for(var j = 0; j < lines.length; j++) {
if(lines[j]) {
requests[i].res.write("data: " + lines[j] + "\n");
}
}
requests[i].res.write("data: Count\: " + broadcastCount + "\n");
requests[i].res.write("\n");
}
console.log("Broadcasted " + broadcastCount + " times to " + requests.length + " user(s).");
}