Playframework Comet socket catch disconnect on client side - javascript

So I'm implementing an application that requires messages to be sent to the browser in real time. Currently this is working fine. When I receive a message I an Untyped Actor similar to the clock example.
What my issue is though is I would like to be able to reconnect the web page when the comet socket gets disconnected. Currently in chrome with comet sockets the loading icon continuously spins. Is there a way I can catch a disconnect message for the iframe/comet socket? Or is there something that I can poll in javascript/jquery? So i can then just reload the page?

If you want to reconnect "the web page" (in other words, make your browser send another request to server, with window.location.reload() or some other method), standard play.libs.Comet.onDisconnected handler is of no use to you - its domain is a server-side, not a client-side.
To make your client-side manage possible blackouts by itself, you may need to implement 'heartbeat' scheme. The client application will ping your server when too much time has been passed since the previous message. One possible way to do this:
var CometProcessor = {
processMessage: function(message) {
console.log(message);
},
handleHeartbeatTimeout: function() {
alert('Heartbeat process timeout');
},
handleHeartbeatError: function() {
alert('Heartbeat process error');
},
timeoutPeriod: 10000, // in milliseconds
timeoutId: 0, // will contain an ID of the current 'checking' timeout
checkHandler: null, // will contain a link to XHR object
checkBeat: function() {
// storing the reference to created XHR object:
this.checkHandler = $.ajax({
url: your_server_url,
// set it to the URL of ping script, that will respond instantly
timeout: 1000,
// this is configurable, but obviously it makes little sense setting this param
// higher than `timeoutPeriod`
success: $.proxy(function() {
// so this particular heartbeat request check went through ok,
// but the next may not be so lucky: we need to schedule another check
this.timeoutId = window.setTimeout(
$.proxy(this.checkBeat, this), this.timeoutPeriod);
}, this),
error: $.proxy(function(x, t) {
if (t === 'timeout') {
this.handleHeartbeatTimeout();
}
else {
this.handleHeartbeatError();
}
}, this)
});
},
message: function(message) {
// when we receive a message, link is obviously functioning,
// so we need to stop all the checking procedures
if (this.checkHandler) {
this.checkHandler.abort();
window.clearTimeout(this.timeoutId);
this.checkHandler = null;
}
processMessage(message); // this is where the actual processing takes place
// when we done with processing, we need to setup the heartbeat again:
this.timeoutId = window.setTimeout(
$.proxy(this.checkBeat, this), this.timeoutPeriod);
}
};
Leveraging this object at server-side is quite easy: you just have to replace the line similar to the one in this example...
Ok.stream(events &> Comet(callback = "parent.cometMessage"))
... with this:
Ok.stream(events &> Comet(callback = "parent.CometProcessor.message"))

Related

Is there an HTTP Connection Timeout in client-side JS/Angular?

I'm using Angular's HttpClient to make HTTP requests and I'd like to specify a timeout for them.
I know I can use HTTPInterceptors and add a timeout to RxJS operators, however, these apply to the whole request which I don't want to abort if data transfer is in progress, only if the browser is hanging while trying to connect.
The kind of timeout I need is available in Node.js for example which is well explained here:
Let's say you called socket.setTimeout(300) to set the timeout as 300
ms, and it took 100 ms for DNS lookup, 100 ms for making a connection
with a remote server, 200 ms for the remote server to send response
headers, 50 ms for transferring the first half of the response body
and another 50 ms for the rest. While the entire request & response
took more than 500 ms, timeout event is not emitted at all.
Is it possible to have a timeout like this in an Angular app?
I looked at the source code for the HttpClient. The code that actually deals with the underlying XMLHttpRequest is the class HttpXhrBackend, in source file named xhr.ts
Unfortunately, HttpXhrBackend just uses the default settings of XMLHttpRequest, and does not provide a way to set the XMLHttpRequest's timeout value.
I have seen suggestions for using RxJS operators to shorten the effective timeout,
but that's a bit of a hack, and doesn't really do what you are asking for.
So, technically, the answer to your question is "No", not with the stock Angular HttpClient, but I suppose that you could create your own implementation of HttpBackend and attempt to inject that.
P.S. This article shows how to provide a custom HttpBackend implementation.
For those still seeking a 'hacky' solution you can create an observable and insert an empty/failure after your desired timeout period:
handleError(error: HttpErrorResponse) {
console.warn('HTTPErrorResponse caught', error);
return observableOf({});
}
async __sendCommandHTTP(cmd: SoftAPCommand) {
const URL = this.options.host + cmd.name;
let result: SoftAPResponse = {
name: cmd.name,
payload: {},
error: false,
};
this.logger.debug('[softap-setup starting request');
await new Promise(resolve => {
const httpEvent: Subject<any> = new Subject<any>();
let returned = false;
const sub = this.http
.get<any>(URL, {})
.pipe(catchError(this.handleError))
.subscribe(data => httpEvent.next(data));
// Our cheeky method to ensure a timeout
setTimeout(async () => {
if (!returned) {
this.logger.info('[softap-setup] timeout on ' + result.name);
httpEvent.next({});
}
}, 5000);
httpEvent.subscribe(data => {
this.logger.info('[softap-setup] response ', data);
returned = true;
switch (cmd.name) {
case 'scan-ap':
if (Object.prototype.hasOwnProperty.call(data, 'scans') && data.scans.length) {
result.payload = data.scans;
} else {
result.error = true;
}
break;
default:
result.payload = data;
break;
}
httpEvent.complete();
resolve();
});
});
return result;
}
Basically either the response or the timeout flags there has been a result. The handleError function also neatly handles any eventual errors that may come along, ie the host isn't available. You could apply other logic in there or even pass along the HTTPErrorResponse object.

Make HTTP requests without opening a new connection

If a browser opens a connection to a remote server, is it possible to access that same connection via Javascript?
I have a small Ethernet module on my network that I program sort of like this (pseudocode):
private var socket
while(true) {
if(socket is disconnected) {
open socket
listen on socket (port 80)
}
if(connection interrupt) {
connect socket
}
if(data receive interrupt) {
serve
}
if(disconnection interrupt) {
disconnect socket
}
}
The point is that it listens on one socket for HTTP requests and serves them.
In my web browser, I can connect to the device, making an HTTP GET request for some HTML/JS that I've written, and it works. A connection is opened on the socket and the files come back as HTTP responses.
Now I want to click a button on the webpage and have the browser send an HTTP POST request over that same connection. In my Javascript, I have (edited and formatted for clarity):
// This function sends an HTTP request
function http(type, url, data, callbacks) {
// make a new HTTP request
var request = new XMLHttpRequest();
// open a connection to the URL
request.open(type, url + (data ? "?" + data : ""));
// add headers
if(type == "POST")
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// register callbacks for the request
Object.keys(callbacks).forEach(function(callback) {
request[callback] = function() {
callbacks[callback](request.responseText);
};
});
// send and return the request
request.send();
return request;
}
// Here is where I call the function
http("POST", // use POST method
"http://192.168.1.99", // IP address of the network device
dataToSend, // the data that needs to be sent
{ // callbacks
onloadend: function(data) {
console.log("success. got: " + data); // print 'success' when the request is done
},
onerror: function(data) {
console.log("There was an error."); // print 'error' when it fails
console.log(data);
}
}
);
The issue here is that this opens a new connection to the device, but I want to use the same socket that the browser is already connected to. Is this possible, and if so, how?
There is no application control inside the browser to decide if a new connection is used for the next request or if an existing connection is used. In fact, it is perfectly normal that the browser will use multiple connections in parallel to the same server and your server has to be able to deal with this.
Since your server architecture seems to be only able to deal with one connection at a time you either would need to change the architecture to handle multiple parallel connections or to make sure that you only need to handle a single connection at a time. The latter could be achieved by not supporting HTTP keep-alive, i.e. by closing the connection immediately after each response. This way a new request will result in a new connection (which is not what you wanted according to your question) but your server will also be able to handle this new connection (which is what you likely ultimately need) since the previous one was closed.

Node.js client request hangs

I have a node.js process that uses a large number of client requests to pull information from a website. I am using the request package (https://www.npmjs.com/package/request) since, as it says: "It supports HTTPS and follows redirects by default."
My problem is that after a certain period of time, the requests begin to hang. I haven't been able to determine if this is because the server is returning an infinite data stream, or if something else is going on. I've set the timeout, but after some number of successful requests, some of them eventually get stuck and never complete.
var options = { url: 'some url', timeout: 60000 };
request(options, function (err, response, body) {
// process
});
My questions are, can I shut down a connection after a certain amount of data is received using this library, and can I stop the request from hanging? Do I need to use the http/https libraries and handle the redirects and protocol switching myself in order the get the kind of control I need? If I do, is there a standardized practice for that?
Edit: Also, if I stop the process and restart it, they pick right back up and start working, so I don't think it is related to the server or the machine the code is running on.
Note that in request(options, callback), the callback will be fired when request is completed and there is no way to break the request.
You should listen on data event instead:
var request = require('request')
var stream = request(options);
var len = 0
stream.on('data', function(data) {
// TODO process your data here
// break stream if len > 1000
len += Buffer.byteLength(data)
if (len > 1000) {
stream.abort()
}
})

Is there any way to handle websocket onmessage handler just like done method handlers attached to XHR deferred objects in jQuery

I'm currently doing this hack to handle the websocket's onmessage.
$scope.wsCon.onMessage = function(result) {
$scope.wsCon.trigger(result.handler, result.data);
};
Problem here is that, onmessage is handling all the incoming requests through websocket.
But I need something like this:
$scope.wsCon.
.send(data)
.done(data, function (result) {
// Deal with the result here
})
.fail(data, function() {
// Show the error message
})
.complete(data, function() {
// Do this always
});
I know this is not achievable in websocket on single connection. But still, is there any way to produce effect something like the way jQuery does?
WebSockets are not request/response based, so since there is no response required when you send a message, what do you expect to happen to finish that promise? the socket flushing the buffer? :) If the browser fails to send the message because the socket is dead, you will get an "onerror" message.
If you need confirmation of the message, or awaiting a response, you need to implement it yourself.
Please take a look at this answer : AngularJS and WebSockets beyond About this $connection service declared in this WebSocket based AngularJS application
Basically it is an example about creating a WebSocket service in AngularJS that can be used to request/response and publish/subscribe.
Basically you can listen for messages:
$connection.listen(function (msg) { return msg.type == "CreatedTerminalEvent"; },
function (msg) {
addTerminal(msg);
$scope.$$phase || $scope.$apply();
});
Listen once (great for request/response):
$connection.listenOnce(function (data) {
return data.correlationId && data.correlationId == crrId;
}).then(function (data) {
$rootScope.addAlert({ msg: "Console " + data.terminalType + " created", type: "success" });
});
And send messages:
$connection.send({
type: "TerminalInputRequest",
input: cmd,
terminalId: $scope.terminalId,
correlationId: $connection.nextCorrelationId()
});

WebSocket inside a WebWorker - UI Thread still blocking

i'm using WebSockets to send data from my node.js server to my clients. Since the data can be kind of large, the UI thread used to block, so no user interaction or video playing was possible during the data was received. That's when i stumbled over WebWorkers, and i also managed to get them work together with WebSockets.
app.js:
...
var worker = new Worker('worker.js');
worker.addEventListener('message', function(e) {
console.log('Worker said: ', e.data);
}, false);
worker.postMessage('init');
...
worker.js:
function initWebSocket() {
var connection = new WebSocket('ws://host:port', ['soap', 'xmpp']);
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
//self.postMessage('Worker received : ' + e.data);
};
};
self.addEventListener('message', function(e) {
switch (e.data) {
case 'init':
initWebSocket();
break;
default:
self.postMessage('Unknown command: ' + e.data);
};
}, false);
All i'm doing so far is receive the data. Of course, later on i intend to do more stuff with it. But my problem is: The UI thread is still blocking when large files arrive. Did i get something wrong here?
UPDATE:
Actually, i have to revise my previous comment. Obviously chrome had cached some of my files i was sending before, so i didn't realize the problem starts already with files way smaller than 300MB (currently, i'm testing a 50MB file). The ui blocks until the file has been completly received. What i'm currently doing is the following: I'm loading an index page with a video playing. Also, on the same page, i put a button which starts a worker. The worker does send an xhr request to the server and gets a 50MB file. So i just dismissed WebSockets for the sake of it. What's happening when i click the button: The video freezes until the complete data has been received. When i do the same and let the worker just calculate numbers in a for-loop, the video keeps playing. So it seems to have something to work with using the network, but not specifically WebSockets. Is it possible that WebWorkers just can't work with network stuff?

Categories