SignalR reconnect to other url - signalr

I need connect to other port after disconnect
Sample:
connected to zxy.com:8080/signalr
disconnect
connected to zxy.com:8081/signalr
disconnect
connected to zxy.com:8082/signalr
Now I work that
function SignalR(port) {
var conn = $.hubConnection(root + ':' + port + '/signalr', { useDefaultPath: false });
manager.hub = conn.createHubProxy('myHub');
conn.stateChanged(state => {
case $.signalR.connectionState.disconnected:
manager.hub.connection.stop();
setTimeout(function () {
port++;
SignalR(port);
}, 5000);
break;
})
It work but i want use default/easy code
function SignalR(port) {
//SET CORRECT port
manager.hub = $.connection.myHub;
$.connection.hub.start().done(function () {
});
$.connection.hub.disconnected(function () {
//DISPOSE CURRENT
setTimeout(function () {
port++;
SignalR(port);
}, 5000);
});
I don't want rewrite all code to
manager.hub.client.someEvent(...)
to
manager.hub.on('someEvent', ...)
manager.hub.server.someMethod();
to
manager.hub.invoke('someMethod', ...)

$.connection.hub.url = host + port + '/signalr';
$.connection.hub.start();

Related

Best Practices for reconnecting a disconnected SignalR client (JS)

I'd like to improve the resilience of my clientside implementation of a signalR client.
Currently, I do this:
hub.server.sendClientNotification(string, appSettings.username());
However occasionally, an exception related to connectivity is thrown because either the server is not responding or the client's internet connection has become unusable.
I'd like to solve this by queuing up requests and then doing something like this:
try {
// pop from queue
hub.server.sendClientNotification(string, appSettings.username());
// success - discard element
} catch (e) {
// requeue element
}
With this kind of implementation, would I need to re-initialize my signalR connection using $.connection.hub.start between disconnects, or can I just continue attempting hub transmits within an interval?
This is what I'm proposing:
var hub = null;
const internalQueue = [];
const states = {
connecting: 0, connected: 1, reconnecting: 2, disconnected: 4
}
const signalrModule = {};
var isInitialized = false;
const connectSignalR = function () {
return new Promise(function (resolve, reject) {
if ($.connection.hub.state == states.connected) {
resolve();
} else {
window.hubReady = $.connection.hub.start({ transport: ["serverSentEvents", "foreverFrame", "longPolling"] });
window.hubReady.done(function () {
isInitialized = true;
resolve();
});
window.onbeforeunload = function (e) {
$.connection.hub.stop();
};
}
})
}
signalrModule.init = function (handleNotification) {
hub = $.connection.appHub;
hub.client.clientNotification = handleNotification;
$.connection.hub.qs = {
"username": appSettings.username()
};
connectSignalR();
}
const tryEmptyQueue = function () {
connectSignalR().then(function() {
if (isInitialized) {
var continueTrying = true;
while (internalQueue.length && continueTrying) {
const nextMessage = internalQueue.shift();
try {
hub.server.sendClientNotification(nextMessage, appSettings.username());
} catch (e) {
internalQueue.push(nextMessage);
continueTrying = false;
}
}
}
})
}
signalrModule.sendClientNotification = function (message) {
if (typeof message != "string") {
message = JSON.stringify(message);
}
if (isInitialized) {
try {
hub.server.sendClientNotification(message, appSettings.username());
tryEmptyQueue();
} catch (e) {
logger.log("SignalR disconnected; queuing request", logger.logLevels.warning);
internalQueue.push(message);
}
} else {
internalQueue.push(message);
};
}
const internalQueueInterval = setInterval(function () {
tryEmptyQueue();
}, 5000);
return signalrModule;
Follow the documentation
In some applications you might want to automatically re-establish a connection after it has been lost and the attempt to reconnect has timed out. To do that, you can call the Start method from your Closed event handler (disconnected event handler on JavaScript clients). You might want to wait a period of time before calling Start in order to avoid doing this too frequently when the server or the physical connection are unavailable. The following code sample is for a JavaScript client using the generated proxy.
$.connection.hub.disconnected(function() {
setTimeout(function() {
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});

SignalR connection closed but still works after angularjs scope destroy

I have a SignalR hub proxy in angularjs factory like this.
var app = angular.module('app');
app.factory("signalRHubProxy", ['$rootScope', "$timeout", function ($rootScope, $timeout) {
function signalRHubProxyFactory(serverUrl, hubName) {
var connection = $.hubConnection(serverUrl);
var proxy = connection.createHubProxy(hubName);
connection.disconnected(function () {
$timeout(function () {
connection.start();
}, 5000)
});
return {
on: function (eventName, callback) {
proxy.on(eventName, function (result) {
$rootScope.$apply(function () {
if (callback) {
callback(result);
}
});
});
connection.start();
},
stop: function () {
connection.stop();
},
connection: connection
};
}
return signalRHubProxyFactory;
}]);
I used connection timeout because sometimes server does not listen, so I can retry.
I am using this factory in my controller:
app.controller("directiveController", function($scope, signalRHubProxy){
var signalRProxy = signalRHubProxy(
"url",
"hubname");
signalRHubProxy.on("datapush", function(data){
});
$scope.$destroy(function(){
signalRHubProxy.stop();
??? how to kill signalr hub
})
})
But when I remove my directive, signalR still works.
I believe when you call stop(), it calls disconnected event, which will just start the connection again. You might need to check if the disconnection was intentional (with a simple boolean variable), then just avoid restarting the connection:
app.factory("signalRHubProxy", ['$rootScope', "$timeout", function ($rootScope, $timeout) {
function signalRHubProxyFactory(serverUrl, hubName) {
var connection = $.hubConnection(serverUrl);
var proxy = connection.createHubProxy(hubName);
var manualStop = false;
connection.connected(function () {
manualStop = false; // reset variable if connected
});
connection.disconnected(function () {
if (manualStop)
return;
$timeout(function () {
connection.start();
}, 5000)
});
return {
on: function (eventName, callback) {
proxy.on(eventName, function (result) {
$rootScope.$apply(function () {
if (callback) {
callback(result);
}
});
});
connection.start();
},
stop: function () {
manualStop = true;
connection.stop();
},
connection: connection
};
}
return signalRHubProxyFactory;
}]);
Also, make sure $scope.$destroy is being called.
Some people recommend calling $.connection.hub.stop(); instead of connection.stop();, although I don't really know whether it has some significant difference.

Make sync method in Meteor js

I tried to make a sync method according to https://themeteorchef.com/tutorials/synchronous-methods
Here's my code:
Server side
Meteor.methods({
sendDataToMixer: function(){
var Future = require('fibers/future');
var SSH = require('simple-ssh');
var future = new Future();
var ssh = new SSH({
host: 'host',
user: 'user',
pass: 'pass'
});
ssh
.exec('mkdir check133', {
out: console.log.bind(console)
})
.start({
success: function(){
future.return("success!");
console.log(future);
},
error: function(err){
future.return(err);
}
});
return future.wait();
}
});
Client side:
Meteor.call('sendDataToMixer', function(err, data){
if(err){
console.log('Ooops from future');
}
else{
console.log(data);
console.log('clicked');
}
});
But this solution doesn't work as a sync one. What am I doing wrong here?

node.js proxy server posts to http server and dies when http server is closed

I'm sending data from client sockets through a proxy server to an http server. When I post this data from proxy server to http, everything works fine, but if I close the http server, the proxy server dies.
I thought the post.end() function would close the request, but apparently not?! Do I have to do some callback magic?
I've attached my console output below, but here's a brief explanation of the steps:
start server: node --harmony_weakmaps server.js
start api(http server): node api.js
start a client(telnet localhost 5280)
client connect message: {"m":"connect","id":"123"}
client message to api: {"m":"x","id":"123"}
kill api process- it blows up
console(server):
>>node --harmony_weakmaps server.js
Starting heartbeat
TCP server listening on 127.0.0.1:5280
HTTP server listening on 127.0.0.1:9002
Connection from 127.0.0.1:49356 id:undefined
received data: {"m":"connect","id":"123"}
id: 123
m: connect
associating uid 123 with socket [object Object]
Heartbeat Time: Tue Feb 14 2012 15:27:08 GMT-0800 (PST)
received data: {"m":"x","id":"123"}
id: 123
m: x
Invalid JSON:{"m":"x","id":"123"}
events.js:48
throw arguments[1]; // Unhandled 'error' event
^
Error: socket hang up
at createHangUpError (http.js:1104:15)
at Socket.onend (http.js:1181:27)
at TCP.onread (net.js:369:26)
console(client, telnet):
>>telnet localhost 5280
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
>>{"m":"connect","id":"123"}
{"m":"connect","id":"123","success":"true"}
{"m":"pulse"}
>>{"m":"x","id":"123"}
{"success":"false","response":"invalid JSON"}
Connection closed by foreign host.
console(api):
>>node api.js
API (HTTP server) listening on 127.0.0.1:8081
Request received: m=x&id=123&success=true
id: 123
m: x
// Then I close it (^C)
server.js(tcp-ip server that posts data from sockets to an http server):
// Launch Instructions
// node --harmony server.js
var net = require('net'); // tcp-server
var http = require("http"); // http-server
// Map of sockets to devices
var id2socket = new Object;
var socket2id = new WeakMap; // allows us to use object as key to hash
// HTTP:POST outbound function
// http://www.theroamingcoder.com/node/111
function postOut(dataToPost){
var querystring = require('querystring');
var http = require('http');
var post_domain = 'localhost';
var post_port = 8081;
var post_path = '/';
var post_data = querystring.stringify(dataToPost);
var post_options = {
host: post_domain,
port: post_port,
path: post_path,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': post_data.length
}
};
var post_req = http.request(post_options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
// write parameters to post body
post_req.write(post_data);
post_req.end();
request.on("response", function (response) {
console.log(response);
});
}
function removeSocketFromMap(id,socket){
console.log("removeSocketFromMap socket:"+socket+" id:"+id);
delete id2socket[id];
socket2id.delete(socket);
//TODO: print map???
}
// Setup a tcp server
var server_plug = net.createServer(
function(socket) {
// Event handlers
socket.addListener("connect", function(conn) {
console.log("Connection from " + socket.remoteAddress + ":" + socket.remotePort + " id:"+socket.id );
});
socket.addListener("data", function(data) {
console.log("received data: " + data);
try {
request = JSON.parse(data);
response = request;
if(request.m !== undefined && request['id'] !== undefined){ // hack on 'id', id is js obj property
console.log("id: "+request['id']);
console.log("m: "+request.m);
if(request.m == 'connect'){
console.log("associating uid " + request['id'] + " with socket " + socket);
id2socket[request['id']] = socket;
socket2id.set(socket, request['id']);
response.success = 'true';
} else {
response.success = 'true';
postOut(request)
}
}
socket.write(JSON.stringify(response));
} catch (SyntaxError) {
console.log('Invalid JSON:' + data);
socket.write('{"success":"false","response":"invalid JSON"}');
}
});
socket.on('end', function() {
id = socket2id.get(socket);
console.log("socket disconnect by id " + id);
removeSocketFromMap(id,socket);
});
socket.on('timeout', function() {
console.log('socket timeout');
});
}
);
// Setup http server
var server_http = http.createServer(
// Function to handle http:post requests, need two parts to it
// http://jnjnjn.com/113/node-js-for-noobs-grabbing-post-content/
function onRequest(request, response) {
request.setEncoding("utf8");
request.content = '';
request.addListener("data", function(chunk) {
request.content += chunk;
});
request.addListener("end", function() {
console.log("Request received: "+request.content);
try {
var json = JSON.parse(request.content);
var id = json['id'];
var m = json['m'];
console.log("id: "+id);
console.log("m: "+m);
// TODO: refactor this into another function
try {
var socket = id2socket[id];
socket.write('{"m":"post"}');
} catch (Error) {
console.log("Cannot find socket with id "+id);
}
} catch(Error) {
console.log("JSON parse error: "+Error)
}
});
}
);
// Heartbeat function
console.log("Starting heartbeat");
var beat_period = 20;
setInterval(function() {
console.log("Heartbeat Time: " + new Date());
for(var id in id2socket) {
var socket = id2socket[id];
try {
socket.write('{"m":"pulse"}');
} catch(Error) {
removeSocketFromMap(id,socket);
}
}
}, beat_period * 1000);
// Fire up the servers
var HOST = '127.0.0.1';
var PORT = 5280;
var PORT2 = 9002;
// accept tcp-ip connections
server_plug.listen(PORT, HOST);
console.log("TCP server listening on "+HOST+":"+PORT);
// accept posts
server_http.listen(PORT2);
console.log("HTTP server listening on "+HOST+":"+PORT2);
api.js(http server):
var http = require("http"); // http-server
var querystring = require('querystring');
// Setup http server
var server_http = http.createServer(
// Function to handle http:post requests, need two parts to it
// http://jnjnjn.com/113/node-js-for-noobs-grabbing-post-content/
function onRequest(request, response) {
request.setEncoding("utf8");
request.content = '';
request.addListener("data", function(chunk) {
request.content += chunk;
});
request.addListener("end", function() {
console.log("Request received: "+request.content);
try {
// Parse incoming JSON
var json = querystring.parse(request.content);
var id = json['id'];
var m = json['m'];
console.log("id: "+id);
console.log("m: "+m);
} catch(Error) {
console.log("JSON parse error: "+Error)
}
});
}
);
// Fire up the servers
var HOST = '127.0.0.1';
var PORT = 8081;
server_http.listen(PORT);
console.log("API (HTTP server) listening on "+HOST+":"+PORT);
Silly me, I'm not up to speed on proper error handling in node. Fixing postOut() did the trick:
function postOut(dataToPost){
var querystring = require('querystring');
var http = require('http');
var post_domain = 'localhost';
var post_port = 8081;
var post_path = '/';
var post_data = querystring.stringify(dataToPost);
var post_options = {
host: post_domain,
port: post_port,
path: post_path,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': post_data.length
}
};
var post_req = http.request(post_options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
// Handle various issues
post_req.on('error', function(error) { // <-------------------------------- Yeah Buddy!!!
console.log('ERROR' + error.message);
// If you need to go on even if there is an error add below line
//getSomething(i + 1);
});
post_req.on("response", function (response) {
console.log(response);
});
// write parameters to post body
post_req.write(post_data);
post_req.end();
request.on("response", function (response) {
console.log(response);
});
}

How to set a timeout on a http.request() in Node?

I'm trying to set a timeout on an HTTP client that uses http.request with no luck. So far what I did is this:
var options = { ... }
var req = http.request(options, function(res) {
// Usual stuff: on(data), on(end), chunks, etc...
}
/* This does not work TOO MUCH... sometimes the socket is not ready (undefined) expecially on rapid sequences of requests */
req.socket.setTimeout(myTimeout);
req.socket.on('timeout', function() {
req.abort();
});
req.write('something');
req.end();
Any hints?
2019 Update
There are various ways to handle this more elegantly now. Please see some other answers on this thread. Tech moves fast so answers can often become out of date fairly quickly. My answer will still work but it's worth looking at alternatives as well.
2012 Answer
Using your code, the issue is that you haven't waited for a socket to be assigned to the request before attempting to set stuff on the socket object. It's all async so:
var options = { ... }
var req = http.request(options, function(res) {
// Usual stuff: on(data), on(end), chunks, etc...
});
req.on('socket', function (socket) {
socket.setTimeout(myTimeout);
socket.on('timeout', function() {
req.abort();
});
});
req.on('error', function(err) {
if (err.code === "ECONNRESET") {
console.log("Timeout occurs");
//specific error treatment
}
//other error treatment
});
req.write('something');
req.end();
The 'socket' event is fired when the request is assigned a socket object.
Just to clarify the answer above:
Now it is possible to use timeout option and the corresponding request event:
// set the desired timeout in options
const options = {
//...
timeout: 3000,
};
// create a request
const request = http.request(options, response => {
// your callback here
});
// use its "timeout" event to abort the request
request.on('timeout', () => {
request.destroy();
});
See the docs:
At this moment there is a method to do this directly on the request object:
request.setTimeout(timeout, function() {
request.abort();
});
This is a shortcut method that binds to the socket event and then creates the timeout.
Reference: Node.js v0.8.8 Manual & Documentation
The Rob Evans anwser works correctly for me but when I use request.abort(), it occurs to throw a socket hang up error which stays unhandled.
I had to add an error handler for the request object :
var options = { ... }
var req = http.request(options, function(res) {
// Usual stuff: on(data), on(end), chunks, etc...
}
req.on('socket', function (socket) {
socket.setTimeout(myTimeout);
socket.on('timeout', function() {
req.abort();
});
}
req.on('error', function(err) {
if (err.code === "ECONNRESET") {
console.log("Timeout occurs");
//specific error treatment
}
//other error treatment
});
req.write('something');
req.end();
There is simpler method.
Instead of using setTimeout or working with socket directly,
We can use 'timeout' in the 'options' in client uses
Below is code of both server and client, in 3 parts.
Module and options part:
'use strict';
// Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js
const assert = require('assert');
const http = require('http');
const options = {
host: '127.0.0.1', // server uses this
port: 3000, // server uses this
method: 'GET', // client uses this
path: '/', // client uses this
timeout: 2000 // client uses this, timesout in 2 seconds if server does not respond in time
};
Server part:
function startServer() {
console.log('startServer');
const server = http.createServer();
server
.listen(options.port, options.host, function () {
console.log('Server listening on http://' + options.host + ':' + options.port);
console.log('');
// server is listening now
// so, let's start the client
startClient();
});
}
Client part:
function startClient() {
console.log('startClient');
const req = http.request(options);
req.on('close', function () {
console.log("got closed!");
});
req.on('timeout', function () {
console.log("timeout! " + (options.timeout / 1000) + " seconds expired");
// Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js#L27
req.destroy();
});
req.on('error', function (e) {
// Source: https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js#L248
if (req.connection.destroyed) {
console.log("got error, req.destroy() was called!");
return;
}
console.log("got error! ", e);
});
// Finish sending the request
req.end();
}
startServer();
If you put all the above 3 parts in one file, "a.js", and then run:
node a.js
then, output will be:
startServer
Server listening on http://127.0.0.1:3000
startClient
timeout! 2 seconds expired
got closed!
got error, req.destroy() was called!
Hope that helps.
For me - here is a less confusing way of doing the socket.setTimeout
var request=require('https').get(
url
,function(response){
var r='';
response.on('data',function(chunk){
r+=chunk;
});
response.on('end',function(){
console.dir(r); //end up here if everything is good!
});
}).on('error',function(e){
console.dir(e.message); //end up here if the result returns an error
});
request.on('error',function(e){
console.dir(e); //end up here if a timeout
});
request.on('socket',function(socket){
socket.setTimeout(1000,function(){
request.abort(); //causes error event ↑
});
});
Elaborating on the answer #douwe here is where you would put a timeout on a http request.
// TYPICAL REQUEST
var req = https.get(http_options, function (res) {
var data = '';
res.on('data', function (chunk) { data += chunk; });
res.on('end', function () {
if (res.statusCode === 200) { /* do stuff with your data */}
else { /* Do other codes */}
});
});
req.on('error', function (err) { /* More serious connection problems. */ });
// TIMEOUT PART
req.setTimeout(1000, function() {
console.log("Server connection timeout (after 1 second)");
req.abort();
});
this.abort() is also fine.
You should pass the reference to request like below
var options = { ... }
var req = http.request(options, function(res) {
// Usual stuff: on(data), on(end), chunks, etc...
});
req.setTimeout(60000, function(){
this.abort();
});
req.write('something');
req.end();
Request error event will get triggered
req.on("error", function(e){
console.log("Request Error : "+JSON.stringify(e));
});
Curious, what happens if you use straight net.sockets instead? Here's some sample code I put together for testing purposes:
var net = require('net');
function HttpRequest(host, port, path, method) {
return {
headers: [],
port: 80,
path: "/",
method: "GET",
socket: null,
_setDefaultHeaders: function() {
this.headers.push(this.method + " " + this.path + " HTTP/1.1");
this.headers.push("Host: " + this.host);
},
SetHeaders: function(headers) {
for (var i = 0; i < headers.length; i++) {
this.headers.push(headers[i]);
}
},
WriteHeaders: function() {
if(this.socket) {
this.socket.write(this.headers.join("\r\n"));
this.socket.write("\r\n\r\n"); // to signal headers are complete
}
},
MakeRequest: function(data) {
if(data) {
this.socket.write(data);
}
this.socket.end();
},
SetupRequest: function() {
this.host = host;
if(path) {
this.path = path;
}
if(port) {
this.port = port;
}
if(method) {
this.method = method;
}
this._setDefaultHeaders();
this.socket = net.createConnection(this.port, this.host);
}
}
};
var request = HttpRequest("www.somesite.com");
request.SetupRequest();
request.socket.setTimeout(30000, function(){
console.error("Connection timed out.");
});
request.socket.on("data", function(data) {
console.log(data.toString('utf8'));
});
request.WriteHeaders();
request.MakeRequest();

Resources