I have a node.js server opening 8000 port. It is a chat server.
I have another PHP server and I use proxy + virtual host so when I go www.phpserver.com/chat It proxies to the node.js server. I did this so I can use ajax to call the node.js server.
Right now, everything works fine when i run the node.js server, however, after a while (a random time frame, not necessarily long or short), the PHP server will crush because it gets an EOF from the node.js server and it's just stuck there until I stop/restart the node.js server.
The error I get is(from php error log):
(70014)End of file found: proxy: error reading status line from remote
server nodeserver.com:8000, referer: https://www.phpserver.com
I asked some professionals and they said it's because of the PHP server sends the request to the node.js server successfully and receives an EOF or fails to receive any response. I don't understand how to fix it tho. What should I do so even the node.js server crushes, it won't crush the PHP server? Should I get rid of the proxy+ajax and starts to use socket.io?
Please advise.
Thank you!
Below is some node codes.
From middleware:
this.events.addListener('update', o_.bind(function(package) {
if(this.clear != 0){
delete this.sessions[this.clear];
}
var _package = package.toJSON();
if(package.type == 'status' && package.status == 'offline') {
var sids = Object.keys(this.sessions), sid, sess;
for(sid in this.sessions) {
sess = this.sessions[sid];
if(sess.data('username') == package.username) {
if(sess.listeners.length)
sess.send(200, {type: 'goodbye'});
delete this.sessions[sid];
break;
}
}
}
}, this));
};
Hub.prototype.destroy = function(sid, fn) {
this.set(sid, null, fn);
};
Hub.prototype.reap = function(ms) {
var threshold = +new Date - ms,
sids = Object.keys(this.sessions);
for(var i = 0, len = sids.length; i < len; ++i) {
var sid = sids[i], sess = this.sessions[sid];
if(sess.lastAccess < threshold) {
this.events.emit('update', new packages.Offline(sess.data('username')));
}
}
};
Hub.prototype.get = function(req, fn) {
if(this.sessions[req.sessionID]) {
fn(null, this.sessions[req.sessionID]);
} else {
this.auth.authenticate(req, o_.bind(function(data) {
if(data) {
var session = new User(req.sessionID, data);
this.set(req.sessionID, session);
this.auth.friends(req, data, o_.bind(function(friends) {
var friends_copy = friends.slice();
o_.values(this.sessions).filter(function(friend) {
return ~friends.indexOf(friend.data('username'));
}).forEach(function(friend) {
var username = friend.data('username');
friends_copy[friends_copy.indexOf(username)] =
[username, friend.status()];
}, this);
session._friends(friends_copy);
console.log("refreshed");
session.events.addListener('status',
o_.bind(function(value, message) {
this.events.emit(
'update',
new packages.Status(session.data('username'),
value,
message)
);
}, this));
this.events.addListener('update',
o_.bind(session.receivedUpdate, session));
this.set(req.sessionID, session);
fn(null, session);
}, this));
} else {
fn();
}
}, this));
}
};
From app.js
#!/usr/bin/env node
var sys = require('sys'),
express = require('express'),
packages = require('./libs/packages'),
fs = require('fs'),
o_ = require('./libs/utils'),
https = require('https');
o_.merge(global, require('./settings'));
try { o_.merge(global, require('./settings.local')); } catch(e) {}
try {
var daemon = require('./libs/daemon/daemon'),
start = function() {
daemon.init({
lock: PID_FILE,
stdin: '/dev/null',
stdout: LOG_FILE,
stderr: LOG_FILE,
umask: 0,
chroot: null,
chdir: '.'
});
},
stop = function() {
process.kill(parseInt(require('fs').readFileSync(PID_FILE)));
};
switch(process.argv[2]) {
case 'stop':
stop();
process.exit(0);
break;
case 'start':
if(process.argv[3])
process.env.EXPRESS_ENV = process.argv[3];
start();
break;
case 'restart':
stop();
start();
process.exit(0);
break;
case 'help':
sys.puts('Usage: node app.js [start|stop|restart]');
process.exit(0);
break;
}
} catch(e) {
sys.puts('Daemon library not found! Please compile ' +
'./libs/daemon/daemon.node if you would like to use it.');
}
var options = {
key: fs.readFileSync('/home/ec2-user/key.pem'),
cert: fs.readFileSync('/home/ec2-user/cert.pem'),
ca: fs.readFileSync('/home/ec2-user/ca.pem'),
};
var app = express();
//app.set('env', 'development');
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(require('./middleware/im')({
maxAge: 30 * 1000,
reapInterval: 20 * 1000,
authentication: require('./libs/authentication/' + AUTH_LIBRARY)
}));
app.set('root', __dirname);
if ('development' == app.get('env')) {
app.set('view engine', 'jade');
app.set('views', __dirname + '/dev/views');
app.stack.unshift({
route: '/dev',
handle: function(req, res, next) {
req.dev = true;
next();
}
});
app.use(express.logger());
require('./dev/app')('/dev', app);
app.use(express.static(
require('path').join(__dirname, '../client')));
app.use(express.errorHandler({dumpExceptions: true, showStack: true}));
}
//app.listen(APP_PORT, APP_HOST);
// Listener endpoint; handled in middleware
app.get('/listen', function(){});
app.post('/message', function(req, res) {
res.find(req.body['to'], function(user) {
if(!user)
return res.send(new packages.Error('not online'));
res.message(user, new packages.Message(
req.session.data('username'),
req.body.body
));
});
});
app.post('/message/typing', function(req, res) {
if(~packages.TYPING_STATES.indexOf('typing' + req.body['state'])) {
res.find(req.body['to'], function(user) {
if(user) {
res.message(user, new packages.Status(
req.session.data('username'),
'typing' + req.body.state
));
}
// Typing updates do not receive confirmations,
// as they are not important enough.
res.send('');
});
} else {
res.send(new packages.Error('invalid state'));
}
});
app.post('/status', function(req, res) {
if(~packages.STATUSES.indexOf(req.body['status'])) {
res.status(req.body.status, req.body.message);
res.send(new packages.Success('status updated'));
} else {
res.send(new packages.Error('invalid status'));
}
});
app.post('/online', function(req, res) {
var d = new Date();
var n = d.getTime() + 30;
req.sessionID.expires = n;
res.status(req.body.status, 'available');
});
app.post('/signoff', function(req, res) {
res.signOff();
res.send(new packages.Success('goodbye'));
});
app.use(function(err, req, res, next){
console.error(err.stack);
res.send(500, 'Error on the node/express server.');
});
https.createServer(options, app).listen(8000);
I can't help you answer your question but I can try to point you in a right direction.
I'm currently working on a Node JS server myself, and I found very useful to have a logger setup.
There are a few of them, but my favorite is Winston so far.
Reference: https://github.com/flatiron/winston
To install Winston for your Node JS server (seems you have already installed a few modules):
npm install winston
Then I have logger module setup as (logger.js):
/**
* Usage:
* - logger.info('sample text');
* - logger.warn('sample text');
* - logger.error('sample text');
*/
// Load modules
var winston = require('winston');
// Create custom logger
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({ json: false, timestamp: true }),
new winston.transports.File({ filename: __dirname + '/debug.log', json: false })
],
exceptionHandlers: [
new (winston.transports.Console)({ json: false, timestamp: true }),
new winston.transports.File({ filename: __dirname + '/exceptions.log', json: false })
],
exitOnError: false
});
// Export logger
module.exports = logger;
Finally I load in Winston logger module into my server scripts by:
var logger = require('./logger');
It will automatically log any exceptions into exceptions.log on your Node JS server location. It helped me out a lot to catch exceptions I haven't noticed before within Node JS unrelated to PHP.
P.S. Also check out socket.io, that may simplify what you are trying to do.
Related
I am working on a gulpfile which already works fine when its processing html/scss/js. Now I want to have my site on PHP-basis and need Browser-Sync to serve my PHP instead of showing "Cannot GET /" in the browser. I know that implies some path issue as it all works, when there is an index.html instead of an index.php in the build folder.
const {src,dest,series,parallel,watch} = require('gulp');
const del = require('del');
const browserSync = require('browser-sync').create();
const sass = require('gulp-sass');
const sourcemaps = require('gulp-sourcemaps');
const origin = './src/';
const destination = './build/';
sass.compiler = require('node-sass');
async function clean(cb) {
await del(destination);
cb();
}
function html(cb) {
src(origin + '*.html').pipe(dest(destination));
cb();
}
function php(cb) {
src(origin + '*.php').pipe(dest(destination));
cb();
}
function css(cb) {
src(origin + 'css/*.scss')
.pipe(sourcemaps.init())
.pipe(
sass({
sourcemap: true,
outputStyle: 'compressed'
})
)
.pipe(dest(destination + 'css'));
cb();
}
function js(cb) {
src(
origin + 'js/lib/jquery.min.js'
).pipe(dest(destination + 'js/lib'));
src(
origin + 'js/*.js',
).pipe(dest(destination + 'js'));
cb();
}
function watcher(cb) {
watch(origin + '**/*.html').on('change',series(html,browserSync.reload));
watch(origin + '**/*.php').on('change',series(php,browserSync.reload));
watch(origin + '**/*.scss').on('change',series(css,browserSync.reload));
watch(origin + '**/*.js').on('change',series(js,browserSync.reload));
cb();
}
function server(cb) {
browserSync.init({
notify: false,
//open: false,
server: {
baseDir: destination
}
})
cb();
}
exports.default = series(clean, parallel(html,php,css,js), server, watcher);
I looked at some solutions which almost all are based on Gulp 3 + gulp-connect-php which seems not to be amongst the Gulp-Plugins anymore. Am I missing that Gulp4 + Node can handle that PHP out of the box? And if yes how?
This is the closest I think I (as a nube) came to a solution: (These are only the changes/additions to the script above)
const phpConnect = require('gulp-connect-php');
function php(cb) {
src(origin + '*.php').pipe(dest(destination));
cb();
}
function watcher(cb) {
watch(origin + '**/*.html').on('change',series(html,browserSync.reload));
watch(origin + '**/*.php').on('change',series(php,browserSync.reload));
watch(origin + '**/*.scss').on('change',series(css,browserSync.reload));
watch(origin + '**/*.js').on('change',series(js,browserSync.reload));
cb();
}
function connectsync(cb) {
phpConnect.server({
port: 8000,
keepalive: true,
base: destination
},
function() {
browsersync({
proxy: '127.0.0.1:8000'
});
});
cb();
}
function server(cb) {
browserSync.init({
notify: false,
open: true,
server: {
baseDir: destination
}
})
cb();
}
exports.default = series(clean, parallel(html,php,css,js), connectsync, server, watcher);
but this seems to get stuck at the browsersync function which - I totally agree - is undefined.
Any help is appreciated very much !!!
After looking into the details of gulp-connect-php it turned out it supports only PHP 5.4 anyway so its not an option. Instead I utilized my XAMPP (while locally installed PHP + Apache might have been a liitle more elegant) and setup browser-sync to proxy to it:
function sync(cb) {
browserSync.init({
proxy: 'http://localhost/malte-podolski.de/build',
port: 3000,
})
cb();
}
I'm new to WebRTC and I don't understand how the signaling works on the server side. I know the request must be two-way. I'm able to send an offer to a PHP page but from there I'm stuck.. How does the other peer pick that offer so that it can generate an answer? I don't want a socket solution because I don't have my own server and I don't want to have a third-party dependency.
I have created an offer and sent that offer to a PHP page. See my code:
var myPeerConnection;
var caller_video=document.getElementById('caller_video');
var receiver_video=document.getElementById('receiver_video');
var mediaConstraints = {
video:true,
audio:false
};
function sendToServer(msg){
var msgJSON = JSON.stringify(msg);
$.ajax({
type:'get',
url:'offer.php',
data:'object='+msgJSON,
beforeSend:function(){
console.log('Sending...');
},
success:function(data){
console.log(data);
}
});
}
function reportError(error){
console.log(error.name);
}
function handleNegotiationNeededEvent(){
myPeerConnection.createOffer().then(function(offer) {
return myPeerConnection.setLocalDescription(offer);
})
.then(function(){ // so here I'm supposed to send an offer
sendToServer({
name: myUsername,
target: targetUsername,
type: "video-offer",
sdp: myPeerConnection.localDescription
});
})
.catch(reportError);
}
function handleICECandidateEvent(event){
if(event.candidate){//send the ICECandidates
sendToServer({
name: myUsername,
target: targetUsername,
type: "new-ice-candidate",
candidate: event.candidate
});
}
}
function handleTrackEvent(event){
console.log(event);
document.getElementById("received_video").srcObject = event.streams[0];
}
function handleRemoveTrackEvent(event){
var stream = document.getElementById("received_video").srcObject;
var trackList = stream.getTracks();
if (trackList.length == 0){
closeVideoCall();
}
}
function handleICEConnectionStateChangeEvent(event){
console.log('ICE connection changed!');
switch(myPeerConnection.iceConnectionState) {
case "closed":
case "failed":
case "disconnected":
closeVideoCall();
break;
}
}
function handleICEGatheringStateChangeEvent(event){
console.log('Is gathering');
console.log(event);
}
function handleSignalingStateChangeEvent(event) {
console.log('Signaling state changed');
switch(myPeerConnection.signalingState) {
case "closed":
closeVideoCall();
break;
}
};
function createPeerConnection() {
var STUN = {
'url': 'stun:stun.l.google.com:19302',
};
var iceServers =
{
iceServers: [STUN]
};
myPeerConnection = new RTCPeerConnection(iceServers);
myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
myPeerConnection.onicecandidate = handleICECandidateEvent;
myPeerConnection.ontrack = handleTrackEvent;
myPeerConnection.onremovetrack = handleRemoveTrackEvent;
myPeerConnection.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
myPeerConnection.onicegatheringstatechange = handleICEGatheringStateChangeEvent;
myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
}
function handleGetUserMediaError(e) {
switch(e.name) {
case "NotFoundError":
alert("Unable to open your call because no camera and/or microphone" +
"were found.");
break;
case "SecurityError":
case "PermissionDeniedError":
// Do nothing; this is the same as the user canceling the call.
break;
default:
alert("Error opening your camera and/or microphone: " + e.message);
break;
}
closeVideoCall();
}
function closeVideoCall(){
//do something to exit the video call
}
//invite the other peer...we want to send our SDP
function invite(evt){
if (myPeerConnection){
console.log('Call already started');
}
else{
//myPeerConnection=new MediaStream();
targetUsername ='Nevil';//Unique other peer username
myUsername='Philip';
createPeerConnection();//this function creates a peer connection//uses the STUN/TURNS servers...updates myPeerConnection variable so it's not null
navigator.mediaDevices.getUserMedia(mediaConstraints)//grab our media constraints
.then(function(localStream) {
caller_video.srcObject =localStream;
caller_video.play();
localStream.getTracks().forEach(track => myPeerConnection.addTrack(track, localStream));
})
.catch(handleGetUserMediaError);
}
}
//we click the call button
document.querySelector('#callBt').addEventListener('click',function(){
invite();
});
I know there must be a way to send back the answer but I would like to just send the offer; that way I will understand how both peers exchange the SDP on the backend. Please try using PHP. If anyone can create a signaling XHR request, I will appreciate it.
I have a example code for make experiments trying to think "how to ""sync"" nodejs and php in a simple chat example.
Here is my NodeJS server:
var redis = require('redis'),
subscriber = redis.createClient(),
publisher = redis.createClient();
//var sckio = require('socket.io').listen(8888);
var http = require('http');
var querystring = require('querystring');
var WebSocketServer = require('ws').Server
var ENCODING = 'utf8';
var tCounter = 0;
/* #################################### */
// Event on "subscribe" to any channel
subscriber.on("subscribe", function (channel, count) {
// Publish to redis server Test Message
publisher.publish("chat", "NODEJS MESSAGE");
});
// Suscrib to redis server
subscriber.on('message', function (channel, json) {
console.log('SUB: ' + channel + ' | ' + json);
console.log('PHP PUSH TO REDIS, AND NODE CAPTURE REDIS PUSH: ' + (getMicrotime(true) - tCounter));
});
subscriber.subscribe('chat'); // Subs to "mysql" channel
/*
var clients = [];
sckio.sockets.on('connection', function (socket) {
clients.push(socket);
publisher.publish("chat", "User connected");
socket.on('message', function (from, msg) {
publisher.publish("chat", msg);
clients.forEach(function (client) {
if (client === socket) return;
client.send(msg);
});
});
socket.on('disconnect', function () {
clients.splice(clients.indexOf(socket), 1);
publisher.publish("chat", "User disconnected");
});
});
*/
var wss = new WebSocketServer({port: 8888, timeout : 500});
var wsClients = [];
wss.on('connection', function(ws) {
ws.AUTH_ID = Math.random();
wsClients.push(ws);
publisher.publish("chat", "User enter");
ws.on('message', function(message) {
wsClients.forEach(function (client) {
client.send(ws.AUTH_ID + ' ' + message);
});
tCounter = getMicrotime(true);
console.log('CALL TO PHP: ' + tCounter);
PostCode('CODE TO PHP FROM NODE', function() {
wsClients.forEach(function (client) {
client.send('PHP SAVE DATA');
});
});
});
ws.on('close', function(message) {
wsClients.splice(wsClients.indexOf(ws), 1);
publisher.publish("chat", "User left");
});
ws.send('HELLO USER!');
});
function getMicrotime(get_as_float) {
var now = new Date().getTime() / 1000;
var s = parseInt(now, 10);
return (get_as_float) ? now : (Math.round((now - s) * 1000) / 1000) + ' ' + s;
}
function PostCode(codestring, callback) {
// Build the post string from an object
var post_data = querystring.stringify({
'output_format': 'json',
'js_code' : codestring
});
// An object of options to indicate where to post to
var post_options = {
host: '127.0.0.1',
port: '80',
path: '/NodeJS/chat_system/php_system.php',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': post_data.length
}
};
// Set up the request
var post_req = http.request(post_options, function(res) {
res.setEncoding(ENCODING);
res.on('data', function (chunk) {
console.log('Response FROM PHP: ' + chunk);
if (typeof callback == 'function') {
callback(chunk);
}
});
});
// post the data
post_req.write(post_data);
post_req.end();
}
Here is my PHP Server
require 'Predis/Autoloader.php';
Predis\Autoloader::register();
function pushToRedis($data) {
try {
$redis = new Predis\Client(array(
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
));
} catch (Exception $e) {
echo "Couldn't connected to Redis";
echo $e->getMessage();
return false;
}
$json = json_encode($data);
$redis->publish("chat", $json);
return true;
}
pushToRedis('PHP PUSH TO REDIS!');
header('Content-Type: application/json');
echo json_encode(array('response' => print_r(array($_REQUEST, $_SERVER), true)));
And my client:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebSockets - Simple chat</title>
<style>
.chat { width: 400px; height: 250px; overflow-y: scroll; }
</style>
</head>
<body>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
var connection = new WebSocket('ws://127.0.0.1:8888');
$(document).ready(function() {
/*
var socket = io.connect('http://127.0.0.1:8888');
socket.on('message', function (data) {
alert(data);
});
socket.send('HELLO!');
*/
connection.onopen = function () {
console.log('connected!');
};
connection.onerror = function (error) {
};
connection.onmessage = function (message) {
$('.chat').append(message.data + '<br>');
$('.chat').scrollTop($('.chat')[0].scrollHeight);
};
$('input[name="text"]').on('keydown', function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
connection.send(msg);
$(this).val('').focus();
}
});
});
</script>
<div class="chat">
</div>
<input type="text" name="text">
</body>
</html>
The problem is the order of the PHP return the response to NodeJS via Redis when the network its bussy.
For example: I send more messages from de javascript client, then, NodeJS call to PHP every message, PHP save the data in MYSQL, and call Redis, NodeJS detect the Redis push and update the clients. But, in some cases, if i send from the Javascript client in loop some messages ( for(0-10000)) I dont reply to others clients in the same order, in cases geting numbers like 200,201,300,202,320,203 in the clients.
I think this is for the PHP delay to response.
My question is How i can manage the responses to update the clients, in the correct order? because this problem can cause, to clients receive the chat messages in wrong order.
But why you want to use php you able to send data to mysql directly using mysql package of nodejs
You may install mysql package via :
npm install mysql
Connection made by :
mysql = require('mysql'),
connection = mysql.createConnection({
host: 'localhost',
user: 'username',
password: 'password',
database: 'database name',
port: 3306
}),
And throw query by using :
var q=connection.query('select * from table);
I have an application written in SSJS (Node) ... This application needs to serve data to the php script that will request by fsockopen .. All right here ... This server needs to collect data from a second server through a persistent connection. How to do this? Making the same process coordinate these connections? Is this possible?
var net = require('net');
/* #############################################
# "CLIENT" Used to connect to data server
# ---------------------------------
#############################################
*/
var clientConnect = net.createConnection(port, host);
clientConnect.setEncoding('utf8');
clientConnect.on('connect', function () {
console.log('Client','Connected to CAGEAPI');
clientConnect.write('user#pass');
});
clientConnectt.on('data', function (data) {
console.log('Client','Data received: ' + data);
});
clientConnect.on('close', function(code) {
console.log('Client','Connection closed');
});
clientConnect.on('error', function (err) {
console.log(err);
});
/* ################################################
#
# "SERVER" Used to serv data to PHPScripts
# --------------------------------
################################################
*/
var handleServer = net.createServer(function(server) {
console.log('Server','CONNECTED: ' + server.remoteAddress +':'+ server.remotePort);
server.on('data', function(data) {
console.log('Server','DATA ' + server.remoteAddress + ': ' + data);
// Write the data back to the socket, the client will receive it as data from the server
server.write('You said "' + data + '"');
});
// Add a 'close' event handler to this instance of socket
server.on('close', function(data) {
console.log('Server','CLOSED: ' + server.remoteAddress +' '+ server.remotePort);
});
}).listen(port2, host2);
Both (Client and server) is working fine... But how to make they talk each other?
I think you're probably after something like this:
/*jslint node: true, white: true */
// Declare constructors
var DataSource, PHPClientServer;
// The DataSource class
// Handles connecting/reconnecting to the data source, and piping endpoints together
(function() {
"use strict";
DataSource = function(net)
{
this.net = net;
};
DataSource.prototype.net = null;
DataSource.prototype.host = 'localhost';
DataSource.prototype.port = 0;
DataSource.prototype.user = '';
DataSource.prototype.pass = '';
DataSource.prototype.socket = null;
DataSource.prototype.currentClient = null;
DataSource.prototype.start = function(host, port, user, pass)
{
if (host !== undefined) {
this.host = host;
}
if (port !== undefined) {
this.port = port;
}
if (user !== undefined) {
this.user = user;
}
if (pass !== undefined) {
this.pass = pass;
}
this.socket = this.net.createConnection(this.port, this.host);
this.socket.on('connect', function () {
console.log('Data source connected');
this.socket.write(this.user + '#' + this.pass);
}.bind(this));
this.socket.on('error', function() {
console.error('Error on data source connection');
this.stop();
this.start();
}.bind(this));
this.socket.on('end', function() {
console.error('Data source connection terminated');
this.stop();
this.start();
}.bind(this));
};
DataSource.prototype.stop = function()
{
this.socket.end();
this.socket = null;
};
DataSource.prototype.attachClient = function(client)
{
console.log('Attaching client to data source');
this.currentClient = client;
this.socket.pipe(this.currentClient);
this.currentClient.pipe(this.socket, {end: false});
};
DataSource.prototype.detachCurrentClient = function()
{
console.log('Detaching client from data source');
this.socket.unpipe(this.currentClient);
this.currentClient.unpipe(this.socket);
this.currentClient = null;
};
DataSource.prototype.hasClient = function()
{
return this.currentClient !== null;
};
}());
// The PHPClientServer class
// Handles the server operations for PHP clients
(function() {
"use strict";
PHPClientServer = function(net, dataSource)
{
this.net = net;
this.dataSource = dataSource;
this.pendingClientStack = [];
};
PHPClientServer.prototype.net = null;
PHPClientServer.prototype.dataSource = null;
PHPClientServer.prototype.host = null;
PHPClientServer.prototype.port = null;
PHPClientServer.prototype.server = null;
PHPClientServer.prototype.pendingClientStack = null;
PHPClientServer.prototype.start = function(host, port)
{
var clientTerminateHandler = function() {
console.log('Client disconnected');
this.dataSource.detachCurrentClient();
if (this.pendingClientStack.length) {
console.log('Attaching next client in queue');
this.dataSource.attachClient(this.pendingClientStack.shift());
}
}.bind(this);
if (host !== undefined) {
this.host = host;
}
if (port !== undefined) {
this.port = port;
}
this.server = this.net.createServer(function(client) {
console.log('Client connected');
client.on('end', clientTerminateHandler);
client.on('error', clientTerminateHandler);
if (this.dataSource.hasClient()) {
console.log('Client added to queue');
this.pendingClientStack.push(client);
} else {
this.dataSource.attachClient(client);
}
}.bind(this));
this.server.listen(this.port, this.host);
};
PHPClientServer.prototype.stop = function()
{
this.server.close();
this.server = null;
};
}());
// Bootstrap
var net, dataSource, server;
net = require('net');
dataSource = new DataSource(net);
dataSource.start('192.168.0.1', 23);
server = new PHPClientServer(net, dataSource);
server.start('0.0.0.0', 12345);
I realise that's a wall of code with minimal explanation, so please ask if there's something you don't understand.
Also, before anyone says it, yes I am fully aware that I am treating a prototypical OOP language as if it were a classical one, Javascript != Java, yada yada yada. I don't care, I like to work with Javascript in this manner.
The symptoms: I have the "push_listener_concurrency" set to "broadcast" but only several (random amount) of subscribers get the message. Was anybody dealing with this type of problem? The test cases are pretty straight-forward I 'POST' to a '/publisher?cid=test' channel and 'GET' from a '/listener?cid=test' everything seems to be working when there's only one client per channel...
Just in case somebody will face the same problem.
There were a problem with headers. To listen the nginx http push module correctly with jQuery you should use a listener that processes the headers correctly here is my code:
function Listener(url, successCallback, failureCallback) {
var scope = this;
var etag = 0, lastModified = 0;
var launched = false;
var failure = false;
this.successTimeout = 0;
this.failureTimeout = 5000;
var getTimeout = function () {
return failure ? this.failureTimeout : this.successTimeout;
};
var listen = function () {
$.ajax(scope.ajaxOptions);
}
var beforeSend = function (jqXHR) {
jqXHR.setRequestHeader("If-None-Match", etag);
jqXHR.setRequestHeader("If-Modified-Since", lastModified);
};
var complete = function (jqXHR) {
var timeout = getTimeout();
etag = jqXHR.getResponseHeader('Etag');
lastModified = jqXHR.getResponseHeader('Last-Modified');
var timeout = jqXHR.statusText == 'success' ? scope.successTimeout : scope.failureTimeout;
if (timeout > 0) {
setTimeout(listen, timeout);
} else {
listen();
}
};
this.ajaxOptions = {
url : url,
type : 'GET',
async : true,
error : failureCallback,
success : successCallback,
dataType : 'json',
complete : complete,
beforeSend : beforeSend,
timeout: 1000 * 60 * 60 * 24
};
this.start = function (timeout) {
if (!launched) {
if (typeof(timeout) == 'undefined' || timeout == 0) {
listen();
} else {
setTimeout(listen, timeout);
}
launched = true;
}
};
}