Atmosphere JS keeps sending get request after socket open - push-notification

I'm using Long Polling with Atmosphere JS for session timeout, so the server tells me when the user has been logged out.
The issue is that once subscribed, Atmosphere JS keeps sending a get request every 60 seconds, which restarts the user session and never logs them out.
I've read the documentation and searched around, but can't see any way to stop this happening. Here is my code:
var socket = atmosphere;
var subSocket;
// subscribe
function subscribe() {
var request = {
url : "/web-service/notifier",
transport: 'long-polling'
};
request.onMessage = function (response) {
var jsonStringArray = response.responseBody.split('|');
// go through each notification and convert from string to object
$.each(jsonStringArray, function(index, elem){
if (elem != ""){
var parsedObject = JSON.parse(elem);
// if notification states user is logged out, log them out
if (parsedObject.action === 'LOGGED_OUT'){
// DO LOGOUT STUFF
}
}
});
};
subSocket = socket.subscribe(request);
}
Thanks for any help.

That's how long-polling works, e.g the connection will be closed/resumed by the server after 60 seconds (you have set that value server side...check your code)
-- Jeanfrancois

Related

How to redirect user to login page instantly after token expires

I am trying to redirect user to login page instantly after token expires and not after the next click how it is now. I have set
PostLogoutRedirectUri, FrontChannelLogoutUri, IdentityTokenLifetime, AccessTokenLifetime
correctly and it works fine redirecting user to login page after expiration of token. But it's not instant and requires a click. Is there a way to redirect user instantly?
I had a similar problem and can show my solution:
Some explaining words:
Using cookie authentication, the user gets a cookie containing an authentication ticket. The ticket is encrypted by the server and contains a lifetime. Every time a request is sent, the security ticket is sent with it and the lifetime is evaluated by the server. Then, if the remaining lifetime is less than half of the configured session lifetime, the lifetime in the authentication ticket is resetted.
My approach:
Knowing the process, the only solution I came up with, was to handle logout (and in my scenario session renewal) on client side. The general approach is to start a JS counter counting to session time configured by the server; Showing a popup for renewal 3 minutes before the lifetime ends and redirecting to the login page one minute before the session lifetime ends.
Here are some snippets I used:
This is part of all of my templates:
jQuery(function{
#if (HttpContext.Current != null && HttpContext.Current.Session["timeOutValue"] != null)
{
#:var timeOutMinute = #HttpContext.Current.Session["timeOutValue"].ToString();
}
else
{
#:var timeOutMinute = 20;
}
localStorage.setItem("timeoutInMinutes", timeOutMinute)
sessionWarning(timeOutMinute);
});
This is my function handling timer and renewal dialogue:
function sessionWarning(timeoutMinutes) {
var warningTime = ((timeoutMinutes - 3) * 60 * 1000);
var sessionTimer = parseInt(localStorage.getItem("sessionTimer"));
if ((sessionTimer !== null) && (sessionTimer < ((timeoutMinutes * 60 * 1000) / 2))) {
warningTime = warningTime - sessionTimer;
}
else {
localStorage.setItem("sessionTimer", "0");
sessionTimer = 0;
}
clearTimeout(timer);
timer = setTimeout(function () {
jQuery("#modal-session-container").modal();
}, warningTime);
intervalTimer = setInterval(function () {
if (isNaN(parseInt(localStorage.getItem("sessionTimer")))) {
localStorage.setItem("sessionTimer", "0");
}
if (!isNaN(timer)) {
localStorage.setItem("sessionTimer", String(parseInt(localStorage.getItem("sessionTimer")) + 1000));
}
}, 1000);
}
I hope this helps!

How to handle socket exception when net is not there?

I want to display an error screen if net is not there. I am not using connectivity package because I don't want continuous check. I just want to handle exception while calling backend api and display the screen. I am unable to catch the exception.
I found this issue and this question about socket exceptions but none seem to help me.
This is how I call my backend api -
callBackendApi() async {
try {
http.Response response = await Future.value(/*api call here*/)
.timeout(Duration(seconds: 90), onTimeout: () {
print('TIME OUT HAPPENED');
});
} catch (exception) {
Fluttertoast.showToast(msg: 'Check internet connection.');
print('Error occurred' + exception.toString());
}
}
I use dio like this:
try {
var formData = FormData.from(Map<String, dynamic>.from(data));
var response = await dio.post(
uri,
data: formData,
);
jsonResponse = json.decode(response.data);
} on DioError catch (e) {
if (DioErrorType.RECEIVE_TIMEOUT == e.type ||
DioErrorType.CONNECT_TIMEOUT == e.type) {
throw CommunicationTimeoutException(
"Server is not reachable. Please verify your internet connection and try again");
} else if (DioErrorType.RESPONSE == e.type) {
// 4xx 5xx response
// throw exception...
} else if (DioErrorType.DEFAULT == e.type) {
if (e.message.contains('SocketException')) {
throw CommunicationTimeoutException('blabla');
}
} else {
throw CommunicationException("Problem connecting to the server. Please try again.");
}
}
My solution is to import 'dart.io' in order to catch SocketException from try block:
import 'package:http/http.dart' as http;
import 'dart:io';
try{
//Handle you network call code block in here
}on SocketException catch(_){
//To handle Socket Exception in case network connection is not available during initiating your network call
}
Well i don't know if my answer will be solving your question but days ago i had a problem little bit likely yours but in my case was using firebase realtime database. I was asking to myself how can i protect my app from network fails like no internet connection available? Well i am not using connectivity package too so i solve this problem with an approach that you already has been try using a timeout for network operations. I will share two snipets with differents approaches that i had implemented to handle this kind of problem adding some comments trying explain the differences between them.
Approach 1 - Setting timeout outside from network request method
Well the snipet below is a simple firebase database request where _viewsRef is a DatabaseReference and the once method do the request and returns me a Future with or without data.
// get users visualization from realtime database and returns a future
static Future<DataSnapshot> getUserVisualizations({#required String uid}) async {
return _viewsRef.child(uid).limitToLast(50).once();
}
In my BLoC component class i am calling the method below and setting a timeout to the future that is returned.
myBlocComponentMethod(){
//.. some work and finally the call
FirebaseUserViewsHelper.getUserVisualizations(uid: _currentUid)
.then(
(dataSnapshot){
if (dataSnapshot.value == null) {
// do some things to handle no data
}
else {
/// handle your data here
});
}
} // setting timeout here is an important point
).timeout( Duration(seconds: Constants.NETWORK_TIMEOUT_SECONDS),
onTimeout: (){
// method to handle a timeout exception and tell to view layer that
// network operation fails
// if we do not implement onTimeout callback the framework will throw a TimeoutException
} );
}
Well what is the point here? In this case if the timeout expires and future is not completed yet onTimeout callback is called and there i can tell to the view layer that network operation fails and show to the user some widget about it. But even with timeout expired the request to firebase database stays happening again and again, it's like the async event of request the database stays on dart event queue. I think this behavior is bad for performance aspects but if you're building your UI using a StreamBuilder with a little logic and code your requested data will be available right when you internet connection is back and with BLoC pattern the UI can respond easily to this event and we don't need provide a refresh button by example to user make the request again. I don't know if this is the right approach to implement this behavior but it works.
Approach 2 - Setting timeout inside from network request method
Below another firebase database request method
static Future<DataSnapshot> readUserNode( {#required String uid} ) async
=> USERS_REFERENCE.child(uid).once()
.timeout( Duration(seconds: Constants.NETWORK_TIMEOUT_SECONDS ) );
//note: Without timeout callback this line will throw a TimeoutException if the time expires
The usage in another BLoc component:
myBlocComponentMethod2(){
for( String uid in iterable ){
FirebaseUserHelper.readUserNode(uid: uid)
.then( (userSnapshot){
if (userSnapshot.value == null){
// do your stuffs
}
else {
// more stuffs to do
}
}).catchError( (error){
// when timeout expired we will catch the TimeoutException HERE and handling telling
// the UI what we need
} );
}
}
The big difference here that i get was in the behavior. In this second case since i put the timeout inside the request method when the timeout expires the request event do not run anymore, it's like that request event is removed from dart event queue. This can be good from performance perspective but now we need provide a refresh button in UI for user do the data again to get data from internet again.
I don't know if this workaround will solve your problem because you tell about SocketException what is not the case that i has described and i don't know what api you are using to make your requests. Anyway i hope that the concepts described in this post helps you implement a solution in your problem.

How to get a timeout callback on a signalr hub client?

I'd like to handle timeout callback on a hub call to garantee that server received my "message", how can I do that?
I looked at signalR wiki, googled about it but found nothing!
That was the way I thought the interface would be:
var hubcon = new HubConnection("myurl", useDefaultUrl: false);
IHubProxy chatHub = hubcon.CreateHubProxy("chatHub");
chatHub.On("timeout", data =>
{
//do something
});
hubcon.Start().Wait(1000);
chatHub.Invoke("EnviarMensagem", new { nome = nome, mensagem = mensagem }).Wait();
hubcon.Stop();
Since you are calling Wait anyway, you can set a timeout and look at the return value to see if the server finished processing your invocation:
if (!chatHub.Invoke("EnviarMensagem", ...).Wait(10000))
{
// The server did not respond to the invocation within 10 seconds
}
http://msdn.microsoft.com/en-us/library/dd270644.aspx

Is there a notification when ASP.NET Web API completes sending to the client

I'm using Web API to stream large files to clients, but I'd like to log if the download was successful or not. That is, if the server sent the entire content of the file.
Is there some way to get a a callback or event when the HttpResponseMessage completes sending data?
Perhaps something like this:
var stream = GetMyStream();
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
// This doesn't exist, but it illustrates what I'm trying to do.
response.OnComplete(context =>
{
if (context.Success)
Log.Info("File downloaded successfully.");
else
Log.Warn("File download was terminated by client.");
});
EDIT: I've now tested this using a real connection (via fiddler).
I inherited StreamContent and added my own OnComplete action which checks for an exception:
public class StreamContentWithCompletion : StreamContent
{
public StreamContentWithCompletion(Stream stream) : base (stream) { }
public StreamContentWithCompletion(Stream stream, Action<Exception> onComplete) : base(stream)
{
this.OnComplete = onComplete;
}
public Action<Exception> OnComplete { get; set; }
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
var t = base.SerializeToStreamAsync(stream, context);
t.ContinueWith(x =>
{
if (this.OnComplete != null)
{
// The task will be in a faulted state if something went wrong.
// I observed the following exception when I aborted the fiddler session:
// 'System.Web.HttpException (0x800704CD): The remote host closed the connection.'
if (x.IsFaulted)
this.OnComplete(x.Exception.GetBaseException());
else
this.OnComplete(null);
}
}, TaskContinuationOptions.ExecuteSynchronously);
return t;
}
}
Then I use it like so:
var stream = GetMyStream();
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContentWithCompletion(stream, ex =>
{
if (ex == null)
Log.Info("File downloaded successfully.");
else
Log.Warn("File download was terminated by client.");
});
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return response;
I am not sure if there is direct signaling that all is ok, but you can use a trick to find out that the connection is exist just before you end it up, and right after you fully send the file.
For example the Response.IsClientConnected is return true if the client is still connected, so you can check something like:
// send the file, make a flush
Response.Flush();
// and now the file is fully sended check if the client is still connected
if(Response.IsClientConnected)
{
// log that all looks ok until the last byte.
}
else
{
// the client is not connected, so maybe have lost some data
}
// and now close the connection.
Response.End();
if the server sent the entire content of the file
Actually there is nothing to do :)
This might sound very simplistic but you will know if an exception is raised - if you care about server delivering and not client cancelling halfway. IsClientConnected is based on ASP.NET HttpResponse not the WebApi.

SignalR recording when a Web Page has closed

I am using MassTransit request and response with SignalR. The web site makes a request to a windows service that creates a file. When the file has been created the windows service will send a response message back to the web site. The web site will open the file and make it available for the users to see. I want to handle the scenario where the user closes the web page before the file is created. In that case I want the created file to be emailed to them.
Regardless of whether the user has closed the web page or not, the message handler for the response message will be run. What I want to be able to do is have some way of knowing within the response message handler that the web page has been closed. This is what I have done already. It doesnt work but it does illustrate my thinking. On the web page I have
$(window).unload(function () {
if (event.clientY < 0) {
// $.connection.hub.stop();
$.connection.exportcreate.setIsDisconnected();
}
});
exportcreate is my Hub name. In setIsDisconnected would I set a property on Caller? Lets say I successfully set a property to indicate that the web page has been closed. How do I find out that value in the response message handler. This is what it does now
protected void BasicResponseHandler(BasicResponse message)
{
string groupName = CorrelationIdGroupName(message.CorrelationId);
GetClients()[groupName].display(message.ExportGuid);
}
private static dynamic GetClients()
{
return AspNetHost.DependencyResolver.Resolve<IConnectionManager>().GetClients<ExportCreateHub>();
}
I am using the message correlation id as a group. Now for me the ExportGuid on the message is very important. That is used to identify the file. So if I am going to email the created file I have to do it within the response handler because I need the ExportGuid value. If I did store a value on Caller in my hub for the web page close, how would I access it in the response handler.
Just in case you need to know. display is defined on the web page as
exportCreate.display = function (guid) {
setTimeout(function () {
top.location.href = 'GetExport.ashx?guid=' + guid;
}, 500);
};
GetExport.ashx opens the file and returns it as a response.
Thank you,
Regards Ben
I think a better bet would be to implement proper connection handling. Specifically, have your hub implementing IDisconnect and IConnected. You would then have a mapping of connectionId to document Guid.
public Task Connect()
{
connectionManager.MapConnectionToUser(Context.ConnectionId, Context.User.Name);
}
public Task Disconnect()
{
var connectionId = Context.ConnectionId;
var docId = connectionManager.LookupDocumentId(connectionId);
if (docId != Guid.Empty)
{
var userName = connectionManager.GetUserFromConnectionId(connectionId);
var user = userRepository.GetUserByUserName(userName);
bus.Publish( new EmailDocumentToUserCommand(docId, user.Email));
}
}
// Call from client
public void GenerateDocument(ClientParameters docParameters)
{
var docId = Guid.NewGuid();
connectionManager.MapDocumentIdToConnection(Context.ConnectionId, docId);
var command = new CreateDocumentCommand(docParameters);
command.Correlationid = docId;
bus.Publish(command);
Caller.creatingDocument(docId);
}
// Acknowledge you got the doc.
// Call this from the display method on the client.
// If this is not called, the disconnect method will handle sending
// by email.
public void Ack(Guid docId)
{
connectionManager.UnmapDocumentFromConnectionId(connectionId, docId);
Caller.sendMessage("ok");
}
Of course this is from the top of my head.

Resources