Yes. Finally I’ve got an arduino board. It’s time to hack a little bit. Today I want to try different things. I want to display in a webpage one value from my arduino board. For example one analog data using a potentiometer. Let’s start.
We are going to use one potentiometer. A potentiometer is a resistor with a rotating contact that forms an adjustable voltage divider. It has three pins. If we connect one pin to 5V power source of our arduino, another one to the ground and another to one A0 (analog input 0), we can read different values depending on the position of potentiometer’s rotating contact.
Arduino has 10 bit analog resolution. That means 1024 possible values, from 0 to 1023. So when our potentiometer gives us 5 volts we’ll obtain 1024 and when our it gives us 0V we’ll read 0. Here we can see a simple arduino program to read this analog input and send data via serial port:
int mem;
void setup() {
Serial.begin(9600);
}
void loop() {
int value = analogRead(A0);
if (value != mem) {
Serial.println(value);
}
mem = value;
delay(100);
}
This program is simple loop with a delay of 100 milliseconds that reads A0 and if value is different than previously read (to avoid sending the same value when nobody is touching the potentiometer) we send the value via serial port (with 9600 bauds)
We can test our program using the serial monitor of our arduino IDE our using another serial monitor.
Now we’re going to create one script to read this serial port data. We’re going to use Python. I’ll use my laptop and my serial port is /dev/tty.usbmodem14231
import serial
arduino = serial.Serial('/dev/tty.usbmodem14231', 9600)
while 1:
print arduino.readline().strip()
Basically we’ve got our backend running. Now we can create a simple frontend.
...
<div id='display'></div>
...
We’ll need websockets. I normally use socket.io but today I’ll use Crossbar.io. Since I hear about it in a Ronny’s talk at deSymfony conference I wanted to use it.
I’ll change a little bit our backend to emit one event
import serial
from os import environ
from twisted.internet.defer import inlineCallbacks
from twisted.internet.task import LoopingCall
from autobahn.twisted.wamp import ApplicationSession, ApplicationRunner
arduino = serial.Serial('/dev/tty.usbmodem14231', 9600)
class SeriaReader(ApplicationSession):
@inlineCallbacks
def onJoin(self, details):
def publish():
return self.publish(u'iot.serial.reader', arduino.readline().strip())
yield LoopingCall(publish).start(0.1)
if __name__ == '__main__':
runner = ApplicationRunner(environ.get("GONZALO_ROUTER", u"ws://127.0.0.1:8080/ws"), u"iot")
runner.run(SeriaReader)
Now I only need to create a crossbar.io server. I will use node to do it
var autobahn = require('autobahn'),
connection = new autobahn.Connection({
url: 'ws://0.0.0.0:8080/ws',
realm: 'iot'
}
);
connection.open();
And now we only need to connect our frontend to the websocket server
$(function () {
var connection = new autobahn.Connection({
url: "ws://192.168.1.104:8080/ws",
realm: "iot"
});
connection.onopen = function (session) {
session.subscribe('iot.serial.reader', function (args) {
$('#display').html(args[0]);
});
};
connection.open();
});
It works but thre’s a problem. The first time we connect with our browser we won’t see the display value until we change the position of the potentiometer. That’s because ‘iot.serial.reader’ event is only emitted when potentiometer changes. No change means no new value. To solve this problem we only need to change a little bit our crossbar.io server. We’ll “memorize” the last value and we’ll expose one method ‘iot.serial.get’ to ask about this value
var autobahn = require('autobahn'),
connection = new autobahn.Connection({
url: 'ws://0.0.0.0:8080/ws',
realm: 'iot'
}
),
mem;
connection.onopen = function (session) {
session.register('iot.serial.get', function () {
return mem;
});
session.subscribe('iot.serial.reader', function (args) {
mem = args[0];
});
};
connection.open();
An now in the frontend we ask for ‘iot.serial.get’ when we connect to the socket
$(function () {
var connection = new autobahn.Connection({
url: "ws://192.168.1.104:8080/ws",
realm: "iot"
});
connection.onopen = function (session) {
session.subscribe('iot.serial.reader', function (args) {
$('#display').html(args[0]);
}).then(function () {
session.call('iot.serial.get').then(
function (result) {
$('#display').htmlresult);
}
);
}
);
};
connection.open();
});
And thats all. The source code is available in my github account. You also can see a demo of the working prototype here
Last summer I collaborated as a technical reviewer in the book “Socket.IO Cookbook” written by Tyson Cadenhead and finally I’ve got the book in my hands
I’m a big fan of real time technologies and I’m normally Socket.io user. Because of that, when people of Packt Publishing contacted me to join to the project as technical reviewer my answer was yes. I’ve got serious problems nowadays to find time to pet projects and extra activities, but if there’re WebSockets inside I cannot resists.
The book is correct and it’s a good starting point to event-based communication with JavaScript. I normally don’t like beginners books (even if I’m a beginner in the technology). I don’t like the books where author explains how to do one thing that I can see how to do it within the website of the. OK. This book isn’t one of those of books. The writer don’t assume reader is a totally newbie. Because of that newbies sometimes can be lost in some chapters, but this exactly the way we all learn new technologies. I like the way Tyson introduces concepts about socket.io.
The book is focused in JavaScript and also uses JavaScript to the backend (with node). Maybe I miss the integration with non-JavaScript environments, but as socket.io is a javascript library I understand that the usage of JavaScript in all application lifecycle is a good approach.
Also those days I was reading and playing a little bit with WebRTC and the book has one chapter about it! #cool
Last days I’ve been playing with OpenUI5. OpenUI5 is a web toolkit that SAP people has released as an open source project. I’ve read several good reviews about this framework, and because of that I started to hack a little bit with it. OpenUI5 came with a very complete set of controls. In this small example I want to use the “table” control. It’s just a datagrid. This days I playing a lot with Angular.js so I wanted to use together OpenUI5’s table control and Angularjs.
I’m not quite sure if it’s a good decision to use together both frameworks. In fact we don’t need Angular.js to create web applications using OpenUI5. OpenUI5 uses internally jQuery, but I wanted to hack a little bit and create one Angularjs directive to enclose one OpenUI5 datagrid.
First of all, we create one index.html. It’s just a boilerplate with angular + ui-router + ui-bootstrap. We also start our OpenUi5 environment with de default theme and including the table library
Then we create a directive enclosing the OpenUI5 needed code within a Angular module
(function () {
'use strict';
angular.module('ng.openui5', [])
.directive('openui5Table', function () {
function renderColumns(columns, oTable) {
for (var i = 0; i <= columns.length; i++) {
oTable.addColumn(new sap.ui.table.Column(columns[i]));
}
}
var link = function (scope, element) {
var oData = scope.model.data,
oTable = new sap.ui.table.Table(scope.model.conf),
oModel = new sap.ui.model.json.JSONModel();
oModel.setData({modelData: oData});
renderColumns(scope.model.columns, oTable);
oTable.setModel(oModel);
oTable.bindRows("/modelData");
oTable.sort(oTable.getColumns()[0]);
oTable.placeAt(element);
scope.$watch('model.data', function (data) {
if (data) {
oModel.setData({modelData: data});
oModel.refresh();
}
}, true);
};
return {
restrict: 'E',
scope: {model: '=ngModel'},
link: link
};
});
}());
And now we can create a simple Angular.js using the ng.openui5 module. In this application we configure the table and fetch the data from an externar API server
I really like WebSockets. I’ve written several posts about them. Today we’re going to speak about something related to WebSockets. Let me explain it a little bit.
Imagine that we build a Web Application with WebSockets. That’s means that when we start the application, we need to connect to the WebSockets server. If our application is a Single-page application, we’ll create one socket per application, but: What happens if we open three tabs with the application within the browser? The answer is simple, we’ll create three sockets. Also, if we reload one tab (a full refresh) we’ll disconnect our socket and reconnect again. Maybe we can handle this situation, but we can easily bypass this disconnect-connect situation with a HTML5 feature called SharedWorkers.
Web Workers allows us to run JavaScript process in background. We also can create Shared Workers. SharedWorkers can be shared within our browser session. That’s means that we can enclose our WebSocket server inside s SharedWorker, and if we open various tabs with our browser we only need one Socket (one socket per session instead one socket per tab).
I’ve written a simple library called gio to perform this operation. gio uses socket.io to create WebSockets. WebWorker is a new HTML5 feature and it needs a modern browser. Socket.io works also with old browsers. It checks if WebWorkers are available and if they isn’t, then gio creates a WebSocket connection instead of using a WebWorker to enclose the WebSockets.
We can see one simple video to see how it works. In the video we can see how sockets are created. Only one socket is created even if we open more than one tab in our browser. But if we open a new session (one incognito session for example), a new socket is created
Here we can see the SharedWorker code:
"use strict";
importScripts('socket.io.js');
var socket = io(self.name),
ports = [];
addEventListener('connect', function (event) {
var port = event.ports[0];
ports.push(port);
port.start();
port.addEventListener("message", function (event) {
for (var i = 0; i < event.data.events.length; ++i) {
var eventName = event.data.events[i];
socket.on(event.data.events[i], function (e) {
port.postMessage({type: eventName, message: e});
});
}
});
});
socket.on('connect', function () {
for (var i = 0; i < ports.length; i++) {
ports[i].postMessage({type: '_connect'});
}
});
socket.on('disconnect', function () {
for (var i = 0; i < ports.length; i++) {
ports[i].postMessage({type: '_disconnect'});
}
});
And here we can see the gio source code:
var gio = function (uri, onConnect, onDisConnect) {
"use strict";
var worker, onError, workerUri, events = {};
function getKeys(obj) {
var keys = [];
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
keys.push(i);
}
}
return keys;
}
function onMessage(type, message) {
switch (type) {
case '_connect':
if (onConnect) onConnect();
break;
case '_disconnect':
if (onDisConnect) onDisConnect();
break;
default:
if (events[type]) events[type](message);
}
}
function startWorker() {
worker = new SharedWorker(workerUri, uri);
worker.port.addEventListener("message", function (event) {
onMessage(event.data.type, event.data.message);
}, false);
worker.onerror = function (evt) {
if (onError) onError(evt);
};
worker.port.start();
worker.port.postMessage({events: getKeys(events)});
}
function startSocketIo() {
var socket = io(uri);
socket.on('connect', function () {
if (onConnect) onConnect();
});
socket.on('disconnect', function () {
if (onDisConnect) onDisConnect();
});
for (var eventName in events) {
if (events.hasOwnProperty(eventName)) {
socket.on(eventName, socketOnEventHandler(eventName));
}
}
}
function socketOnEventHandler(eventName) {
return function (e) {
onMessage(eventName, e);
};
}
return {
registerEvent: function (eventName, callback) {
events[eventName] = callback;
},
start: function () {
if (!SharedWorker) {
startSocketIo();
} else {
startWorker();
}
},
onError: function (cbk) {
onError = cbk;
},
setWorker: function (uri) {
workerUri = uri;
}
};
};
And here the application code:
(function (gio) {
"use strict";
var onConnect = function () {
console.log("connected!");
};
var onDisConnect = function () {
console.log("disconnect!");
};
var ws = gio("http://localhost:8080", onConnect, onDisConnect);
ws.setWorker("sharedWorker.js");
ws.registerEvent("message", function (data) {
console.log("message", data);
});
ws.onError(function (data) {
console.log("error", data);
});
ws.start();
}(gio));
I’ve also created a simple webSocket server with socket.io. In this small server there’s a setInterval function broadcasting one message to all clients per second to see the application working
WebSockets are great. We can start a persistent connection from our browser to our server and use this connection to send real time notifications to our users. Normally when we integrate WebSockets with an existing Web application, we need to face with one slight problem. Our Web application runs on a Web server (imagine, for example one Silex application). We can use a login form and ensure all requests are authorized (using a security layer). This problem is solved years ago. We can use Basic HTTP authentification, Digtest authentification, a session based authentication, token based authentificatio, OAuth, … The problem arrives when we add WebSocket server. WebSocket server is another serve. We can use node.js, ruby, or even PHP with Rachet. But how we can ensure that WebSocket server’s requests are also authenticated? We can try to share our authentification provider between both servers, but this solution is quite “exotic”. That was the idea behind my blog post: post some time ago. I’ve been thinkin a lot about it, and also read posts and speak with colleages about this subject. Finally I’m using the following solution. Let me explain it.
Websockets are bi-directional. We can get messages in the browser and send them from browser to server. Basically the solution is to disable the messages from the browser to the server via WebSockets. In fact HTML5 provides another tool to do that called Server Side Events (aka SSE), but SSE aren’t as widely used as WebSockets. Because of that I preffer to use WebSockets (without using the browser-to-server chanel) instead of SSE.
Let’s create a simple Silex application:
class Application extends Silex\Application
{
use Silex\Application\TwigTrait;
}
$app = new Application();
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__ . '/../views',
));
$app->get('/', function () use ($app) {
return $app->render('home.twig');
});
$app->run();
And our main template with html file
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<script src="//localhost:8080/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('//localhost:8080');
socket.on('id1', function (data) {
console.log("mensage from websocket: " + data);
});
</script>
</body>
</html>
Now we have Silex application that connects to a WebSockets server. I will use socket.io to build the WebSocket server:
Whit this ultra minimal configuration we can connect from Silex application to WebSocket server and our web application will listen to messages marked as’id1′ from the WebSocket server but, how can we do to send messages? As I said before we only rely on Silex application (in this example there isn’t any security layer, but we can use our custom login). The trick is to create a new server within our node.js server. Start this server at localhost and perform a curl request from our Silex Application to our node.js server to send the WebSockets push notifications. The idea is:
User clicks a link in our html (generated by our Silex application)
This request is a standard Silex request (using our security layer)
Then Silex performs a curl request to node.js server.
If our Silex application and node.js application are in the same server we will create a new server at localhost. In this example we are going to use Express to do that.
Express server will handle requests from our Silex application (not from any other host) and will send WebSocket messages
Now we need to add another route to our Silex application
use Symfony\Component\HttpFoundation\Response;
$app->get('/emit/{id}/{message}', function ($id, $message) use ($app) {
$s = curl_init();
curl_setopt($s, CURLOPT_URL, "http://localhost:26300/emit/{$id}/{$message}");
curl_setopt($s, CURLOPT_RETURNTRANSFER, true);
$content = curl_exec($s);
$status = curl_getinfo($s, CURLINFO_HTTP_CODE);
curl_close($s);
return new Response($content, $status);
});
And that’s all. Our Request from Silex arrives to WebSocket emmiter using a “secure” layer. OK, now you can said: yes, but anybody can connect to the WebSocket server and listen to ‘id1’ chanel, without any restriction. Yes, it’s true. But here you can use different solutions to ensure privacy. For example you can use a “non-obvious” chanel name based on cryptografic funcions. It’s not 100% secure, but it’s the same security layer than the standard session based security mechanism. If we know the cookie name we can perform a session hijacking attack and gain access to secure areas (without knowing the login credentials). We can generate chanel names like this: 7265cfe8fe3daa4c5069d609a0312dd2 with our Silex Application and send to the browser with an AJAX request.
I’ve created an small screencast to see the prototype in action. (source code in my github account)
In the screencast we can see how to install the prototype from github, install PHP’s vendors and the node js modules. We also can see how websocket works with two browser instances, and how to send messages directly accesing to Express application using localhost interface (and an error when I try to reach to Express server using a different network interface)
Today I’m playing with the HTML5’s WebWorkers. Since our JavaScript code runs within a single thread in our WebBrowser, heavy scripts can lock the execution of our code. HTML5 gives us one tool called WebWorkers to allow us to run different threads within our Application.
Today I’m playing with one small example (that’s just an experiment). I want to create a pool of WebWebworkers and use them as a simple queue.
The usage of the library is similar than the usage of WebWorkers. The following code shows how to start the pool with 10 instances of our worker “js/worker.js”
// new worker pool with 10 instances of the worker
var pool = new WorkerPool('js/worker.js', 10);
// register callback to worker's onmessage event
pool.registerOnMessage(function (e) {
console.log("Received (from worker): ", e.data);
});
“js/worker.js” is a standard WebWorker. In this example our worker perform XHR request to a API server (in this case one Silex application)
importScripts('ajax.js');
self.addEventListener('message', function (e) {
var data = e.data;
switch (data.method) {
case 'GET':
getRequest(data.resource, function(xhr) {
self.postMessage({status: xhr.status, responseText: xhr.responseText});
});
break;
}
}, false);
WebWorkers runs in different scope than a traditional browser application. Not all JavaScript objects are available in the webworker scpope. For example we cannot access to “window” and DOM elements, but we can use XMLHttpRequest. In our experimente we’re going to preform XHR requests from the webworker.
The library creates a queue with the variable number of workers:
var WorkerPool;
WorkerPool = (function () {
var pool = {};
var poolIds = [];
function WorkerPool(worker, numberOfWorkers) {
this.worker = worker;
this.numberOfWorkers = numberOfWorkers;
for (var i = 0; i < this.numberOfWorkers; i++) {
poolIds.push(i);
var myWorker = new Worker(worker);
+function (i) {
myWorker.addEventListener('message', function (e) {
var data = e.data;
console.log("Worker #" + i + " finished. status: " + data.status);
pool[i].status = true;
poolIds.push(i);
});
}(i);
pool[i] = {status: true, worker: myWorker};
}
this.getFreeWorkerId = function (callback) {
if (poolIds.length > 0) {
return callback(poolIds.pop());
} else {
var that = this;
setTimeout(function () {
that.getFreeWorkerId(callback);
}, 100);
}
}
}
WorkerPool.prototype.postMessage = function (data) {
this.getFreeWorkerId(function (workerId) {
pool[workerId].status = false;
var worker = pool[workerId].worker;
console.log("postMessage with worker #" + workerId);
worker.postMessage(data);
});
};
WorkerPool.prototype.registerOnMessage = function (callback) {
for (var i = 0; i < this.numberOfWorkers; i++) {
pool[i].worker.addEventListener('message', callback);
}
};
WorkerPool.prototype.getFreeIds = function () {
return poolIds;
};
return WorkerPool;
})();
The API server is a simple Silex application. This application also enables cross origin (CORS). You can read about it here.
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$app = new Application();
$app->get('/hello', function () use ($app) {
error_log("GET /hello");
sleep(2); // emulate slow process
return $app->json(['method' => 'GET', 'response' => 'OK']);
});
$app->after(function (Request $request, Response $response) {
$response->headers->set('Access-Control-Allow-Origin', '*');
});
$app->run();
In one of my previous posts I wrote about Real time notifications with PHP. I wanted to create a simple comet system fully written in PHP and JavaScript. It worked but as Scott Mattocks told me in a comment this implementation was still just doing short polling. The performance with this solution may be bad in a medium/hight traffic site. This days I’m playing with node.js and I want to create a simple test. I want to do exactly the same than the previous post but now with node.js instead of my PHP+js test. Let’s start
Now I want to use socket.io instead of pure web-sockets like my previous posts about node.js. For those who don’t know, socket.io is amazing library that allows us to use real-time technologies within every browsers (yes even with IE6). It uses one technology or another depending on the browser we are using, with the same interface for the developer. That’s means if we’re using Google Chrome we will use websockets, but if our browser does’t support them, socket.io will choose another supported transports. Definitely socket.io is the jQuery of the websockets. The supporter transports are:
WebSocket
Adobe Flash Socket
AJAX long polling
AJAX multipart streaming
Forever Iframe
JSONP Polling
First we create our node.js server. A really simple one.
var http = require('http');
var io = require('socket.io');
server = http.createServer(function(req, res){
});
server.listen(8080);
// socket.io
var socket = io.listen(server);
socket.on('connection', function(client){
client.on('message', function(msg){
socket.broadcast(msg);
})
});
This server will broadcast the message received from the browser to all connected clients.
As we can see we are including the js script called socket.io/socket.io.js. This script is served by our node server.
In fact we can use our node.js to serve everything (HTML, js, CSS) but in our example we will use only node.js for real-time stuff. Apache will serve the rest of the code (only a HTML file in this case).
And that’s all. Those few lines perform the same thing than our PHP and js code in the other post’s example. Our node.js implementation is definitely smarter than the PHP one. The socket.io library also allows us to use the example with all browser. Same code and without any browser nightmare (just like jQuery when we work with DOM).
Here I have a little screencast with the working example. As we will see there We will connect to the node server with firefox and chrome. Firefox will use xhr multipart and Chrome will use Websokets.
Another important issue of socket.io library is that we forget about the reconnection to the web-socket server, if something wrong happens (as we can see in Real time monitoring PHP applications with web-sockets and node.js). If we use raw WebSocket implementations and our connection with the web-socket server crashes or if we stop the server, our application will raise disconnect event and we need to create something to reconnect to the server. socket.io does it for us. With our small piece of JavaScript code we will get a high performance real-time applicatrion. Node is cool. Really cool. Kinda wierd at the beginning but the learning effort will be worthwhile. A few js lines and a real time applications.
I’ve got a problem within our node.js application. If we’ve got some kind of security within our application (imagine for example it’s behind a session based auth form) we need to share this security layer with our node.js server to ensure that non authenticated users aren’t allowed to use our websockets. I don’t know how to do it just now, but I’m investigating. Do you have any idea?
Full code Code at github. Ensure you’re using the stable version of node.js. With the last versión available on github of node.js there’s a bug and server dies when we connect with Google Chrome.
Continuing with my experiments of node.js, this time I want to create a Web console. The idea is simple. I want to send a few command to the server and I display the output inside the browser. I can do it entirely with PHP but I want to send the output to the browser as fast as they appear without waiting for the end of the command. OK we can do it flushing the output in the server but this solution normally crashes if we keep the application open for a long time. WebSockets again to the rescue. If we need a cross-browser implementation we need the socket.io library. Let’s start:
The node server is a simple websocket server. In this example we will launch each command with spawn function (require(‘child_process’).spawn) and send the output within the websoket. Simple and pretty straightforward.
var sys = require('sys'),
http = require('http'),
url = require('url'),
spawn = require('child_process').spawn,
ws = require('./ws.js');
var availableComands = ['ls', 'ps', 'uptime', 'tail', 'cat'];
ws.createServer(function(websocket) {
websocket.on('connect', function(resource) {
var parsedUrl = url.parse(resource, true);
var rawCmd = parsedUrl.query.cmd;
var cmd = rawCmd.split(" ");
if (cmd[0] == 'help') {
websocket.write("Available comands: \n");
for (i=0;i<availableComands.length;i++) {
websocket.write(availableComands[i]);
if (i< availableComands.length - 1) {
websocket.write(", ");
}
}
websocket.write("\n");
websocket.end();
} else if (availableComands.indexOf(cmd[0]) >= 0) {
if (cmd.length > 1) {
options = cmd.slice(1);
} else {
options = [];
}
try {
var process = spawn(cmd[0], options);
} catch(err) {
console.log(err);
websocket.write("ERROR");
}
websocket.on('end', function() {
process.kill();
});
process.stdout.on('data', function(data) {
websocket.write(data);
});
process.stdout.on('end', function() {
websocket.end();
});
} else {
websocket.write("Comand not available. Type help for available comands\n");
websocket.end();
}
});
}).listen(8880, '127.0.0.1');
The web client is similar than the example of my previous post
var timeout = 5000;
var wsServer = '127.0.0.1:8880';
var ws;
function cleanString(string) {
return string.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
function pad(n) {
return ("0" + n).slice(-2);
}
var cmdHistory = [];
function send(msg) {
if (msg == 'clear') {
$('#log').html('');
return;
}
try {
ws = new WebSocket('ws://' + wsServer + '?cmd=' + msg);
$('#toolbar').css('background', '#933');
$('#socketStatus').html("working ... [<a href='#' onClick='quit()'>X</a>]");
cmdHistory.push(msg);
$('#log').append("<div class='cmd'>" + msg + "</div>");
console.log("startWs:");
} catch (err) {
console.log(err);
setTimeout(startWs, timeout);
}
ws.onmessage = function(event) {
$('#log').append(util.toStaticHTML(event.data));
window.scrollBy(0, 100000000000000000);
};
ws.onclose = function(){
//console.log("ws.onclose");
$('#toolbar').css('background', '#65A33F');
$('#socketStatus').html('Type your comand:');
}
}
function quit() {
ws.close();
window.scrollBy(0, 100000000000000000);
}
util = {
urlRE: /https?:\/\/([-\w\.]+)+(:\d+)?(\/([^\s]*(\?\S+)?)?)?/g,
// html sanitizer
toStaticHTML: function(inputHtml) {
inputHtml = inputHtml.toString();
return inputHtml.replace(/&/g, "&")
.replace(/</g, "<")
.replace("/n", "<br/>")
.replace(/>/g, ">");
},
//pads n with zeros on the left,
//digits is minimum length of output
//zeroPad(3, 5); returns "005"
//zeroPad(2, 500); returns "500"
zeroPad: function (digits, n) {
n = n.toString();
while (n.length < digits)
n = '0' + n;
return n;
},
//it is almost 8 o'clock PM here
//timeString(new Date); returns "19:49"
timeString: function (date) {
var minutes = date.getMinutes().toString();
var hours = date.getHours().toString();
return this.zeroPad(2, hours) + ":" + this.zeroPad(2, minutes);
},
//does the argument only contain whitespace?
isBlank: function(text) {
var blank = /^\s*$/;
return (text.match(blank) !== null);
}
};
$(document).ready(function() {
//submit new messages when the user hits enter if the message isnt blank
$("#entry").keypress(function (e) {
console.log(e);
if (e.keyCode != 13 /* Return */) return;
var msg = $("#entry").attr("value").replace("\n", "");
if (!util.isBlank(msg)) send(msg);
$("#entry").attr("value", ""); // clear the entry field.
});
});
And that’s all. In fact we don’t need any line of PHP to perform this web console. Last year I tried to do something similar with PHP but it was a big mess. With node those kind of jobs are trivial. I don’t know if node.js is the future or is just another hype, but it’s easy. And cool. Really cool.
You can see the full code at Github here. Anyway you must take care if you run this application on your host. You are letting user to execute raw unix commands. A bit of security layer would be necessary.
The inspection of the error logs is a common way to detect errors and bugs. We also can show errors on-screen within our developement server, or we even can use great tools like firePHP to show our PHP errors and warnings inside our firebug console. That’s cool, but we only can see our session errors/warnings. If we want to see another’s errors we need to inspect the error log. tail -f is our friend, but we need to surf against all the warnings of all sessions to see our desired ones. Because of that I want to build a tool to monitor my PHP applications in real-time. Let’s start:
What’s the idea? The idea is catch all PHP’s errors and warnings at run time and send them to a node.js HTTP server. This server will work similar than a chat server but our clients will only be able to read the server’s logs. Basically the applications have three parts: the node.js server, the web client (html5) and the server part (PHP). Let me explain a bit each part:
The node Server
Basically it has two parts: a http server to handle the PHP errors/warnings and a websocket server to manage the realtime communications with the browser. When I say that I’m using websockets that’s means the web client will only work with a browser with websocket support like chrome. Anyway it’s pretty straightforward swap from a websocket sever to a socket.io server to use it with every browser. But websockets seems to be the future, so I will use websockets in this example.
The http server:
http.createServer(function (req, res) {
var remoteAdrress = req.socket.remoteAddress;
if (allowedIP.indexOf(remoteAdrress) >= 0) {
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Ok\n');
try {
var parsedUrl = url.parse(req.url, true);
var type = parsedUrl.query.type;
var logString = parsedUrl.query.logString;
var ip = eval(parsedUrl.query.logString)[0];
if (inspectingUrl == "" || inspectingUrl == ip) {
clients.forEach(function(client) {
client.write(logString);
});
}
} catch(err) {
console.log("500 to " + remoteAdrress);
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('System Error\n');
}
} else {
console.log("401 to " + remoteAdrress);
res.writeHead(401, {
'Content-Type': 'text/plain'
});
res.end('Not Authorized\n');
}
}).listen(httpConf.port, httpConf.host);
and the web socket server:
var inspectingUrl = undefined;
ws.createServer(function(websocket) {
websocket.on('connect', function(resource) {
var parsedUrl = url.parse(resource, true);
inspectingUrl = parsedUrl.query.ip;
clients.push(websocket);
});
websocket.on('close', function() {
var pos = clients.indexOf(websocket);
if (pos >= 0) {
clients.splice(pos, 1);
}
});
}).listen(wsConf.port, wsConf.host);
If you want to know more about node.js and see more examples, have a look to the great site: http://nodetuts.com/. In this site Pedro Teixeira will show examples and node.js tutorials. In fact my node.js http + websoket server is a mix of two tutorials from this site.
The web client.
The web client is a simple websockets application. We will handle the websockets connection, reconnect if it dies and a bit more. I’s based on node.js chat demo
var timeout = 5000;
var wsServer = '192.168.2.2:8880';
var unread = 0;
var focus = false;
var count = 0;
function updateCount() {
count++;
$("#count").text(count);
}
function cleanString(string) {
return string.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
function updateUptime () {
var now = new Date();
$("#uptime").text(now.toRelativeTime());
}
function updateTitle(){
if (unread) {
document.title = "(" + unread.toString() + ") Real time " + selectedIp + " monitor";
} else {
document.title = "Real time " + selectedIp + " monitor";
}
}
function pad(n) {
return ("0" + n).slice(-2);
}
function startWs(ip) {
try {
ws = new WebSocket("ws://" + wsServer + "?ip=" + ip);
$('#toolbar').css('background', '#65A33F');
$('#socketStatus').html('Connected to ' + wsServer);
//console.log("startWs:" + ip);
//listen for browser events so we know to update the document title
$(window).bind("blur", function() {
focus = false;
updateTitle();
});
$(window).bind("focus", function() {
focus = true;
unread = 0;
updateTitle();
});
} catch (err) {
//console.log(err);
setTimeout(startWs, timeout);
}
ws.onmessage = function(event) {
unread++;
updateTitle();
var now = new Date();
var hh = pad(now.getHours());
var mm = pad(now.getMinutes());
var ss = pad(now.getSeconds());
var timeMark = '[' + hh + ':' + mm + ':' + ss + '] ';
logString = eval(event.data);
var host = logString[0];
var line = "<table class='message'><tr><td width='1%' class='date'>" + timeMark + "</td><td width='1%' valign='top' class='host'><a href=?ip=" + host + ">" + host + "</a></td>";
line += "<td class='msg-text' width='98%'>" + logString[1]; + "</td></tr>";
if (logString[2]) {
line += "<tr><td> </td><td colspan='3' class='msg-text'>" + logString[2] + "</td></tr>";
}
$('#log').append(line);
updateCount();
window.scrollBy(0, 100000000000000000);
};
ws.onclose = function(){
//console.log("ws.onclose");
$('#toolbar').css('background', '#933');
$('#socketStatus').html('Disconected');
setTimeout(function() {startWs(selectedIp)}, timeout);
}
}
$(document).ready(function() {
startWs(selectedIp);
});
The server part:
The server part will handle silently all PHP warnings and errors and it will send them to the node server. The idea is to place a minimal PHP line of code at the beginning of the application that we want to monitor. Imagine the following piece of PHP code
$a = $var[1];
$a = 1/0;
class Dummy
{
static function err()
{
throw new Exception("error");
}
}
Dummy1::err();
it will throw:
A notice: Undefined variable: var
A warning: Division by zero
An Uncaught exception ‘Exception’ with message ‘error’
So we will add our small library to catch those errors and send them to the node server
include('client/NodeLog.php');
NodeLog::init('192.168.2.2');
$a = $var[1];
$a = 1/0;
class Dummy
{
static function err()
{
throw new Exception("error");
}
}
Dummy1::err();
The script will work in the same way than the fist version but if we start our node.js server in a console:
$ node server.js
HTTP server started at 192.168.2.2::5672
Web Socket server started at 192.168.2.2::8880
We will see those errors/warnings in real-time when we start our browser
Here we can see a small screencast with the working application: