Monolog with Bot Framework - botframework

We are using the Microsoft Bot Framework for our chat bot. Our Message Controller is standard :
public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
HttpResponseMessage response;
try
{
if (activity.Type == ActivityTypes.Message)
{
//do some stuff
await Conversation.SendAsync(activity, () => new RootDialog());
}
else
{
HandleSystemMessage(activity);
}
response = this.Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
//do some catching
}
return response;
}
Sometimes, the bot needs to have long monologs. If it takes too long, we receive a 502 Bad Gateway error.
Any solution for that ?

Bot Framework calls time out after 15 seconds. Your bot must return a successful HTTP status code within 15 seconds for Direct Line to consider the message successfully sent.
If your bot does a lot of offline work or sends multiple messages, you should move that work to a background thread so the incoming request completes within 15 seconds.
Here's a snippet that correctly handles loads hosted inside ASP.Net and other hosting environments.
if (HostingEnvironment.IsHosted)
{
HostingEnvironment.QueueBackgroundWorkItem(c => DoWorkAsync());
}
else
{
Task.Run(() => DoWorkAsync());
}
...
private async Task DoWorkAsync()
{
// do some stuff
}
Task.Run is simpler but HostingEnvironment.QueueBackgroundWorkItem prevents ASP.Net from tearing down your process before this work is complete.

Related

context.sendActivity sent from incoming part of middleware are not going through onSendActivities function

In middleware I am sending a few messages to the user on checking some conditions...
However these are not going through onSendActivities portion of the middleware... (with or without shortcircuiting.... ) i.e. calling next() all the time and skipping calling next() sometimes...
Is this a bug ? or built in by purpose.. Atleast expected all messages sent from incoming part of the middleware function to go through this function.
async onTurn( (context, next) => {
//....
await context.sendActivity(`whatever`)
this.Bot.adapter.continueConversation(userRef, async turnContext => {
// the below one does not show up in console
await turnContext.sendActivity(`Another message to user`);
})
await context.onSendActivities( (sendContext, activities, sendNext) => {
// expected both above sent to hit here...
console.log(`activities : ${activities.map(a => a.text).join(',')}`)
await sendNext()
})
await next()
})
You need to cycle thru the activities object in the onSendActivities() method to log the activity. If you are looking to act on a particular activity, then you can parse each one looking for whichever flag or value you need to match on, then perform your action.
await context.sendActivity(`whatever`);
await context.onSendActivities(async (sendContext, activities, sendNext) => {
for (let activity of activities) {
console.log('SEND ACTIVITY ', activity);
}
return await sendNext();
});
Hope of help!

Await signalr message in client

Is it possible for a SignalR client send a message to the server and then to await for a seperate message (not a return value) from the server?
The theory;
Client1 send message1 to Server and "waits" for the response.
Server processes some logic
Server sends message2 to Client1 and Client1 executes the waiting code.
Call to the server:
$.connection.myhub.server.myRequest(id).done(()==>{
// myRequest is done;
// the server has received the request but hasn't processed it yet.
// I want put some *async* code here to do something when the server has triggered $.connection.myhub.client.myResponse(id, someParam);
});
Callback to the client:
$.connection.myhub.client.myResponse(originalId, somePassedBackValue);
Can I use Async/Await, or wrap this in a Promise somehow?
If this isn't acheivable in SignalR are there anyother socket libraries that might be used instead?
You can do something, like the following:
Imagine you have a client that joins a group, updates a table and then notifies the client that it has joined.
Client
msgHub.server.joinGroup(id).done(function () {
console.log("Joined Group");
serverCallFinished = true;
})
msgHub.client.displayTable = function (table) {
display(table);
}
Hub
public async Task JoinGroup(string practiceId)
{
try
{
await Groups.Add(Context.ConnectionId, practiceId);
//Add new table
var table = new Table(practiceId)
await UpdateTable("SomeGroup", table);
}
catch (Exception e)
{
throw;
}
}
public async Task UpdateInfo(string groupName, string table)
{
//await some logic
Clients.Group(groupName).updateTable(table);
}
Update info will call the client with message2 in this case a table that it wants to display to the client. When it finishes the it will return from its awaited state by JoinGroup which will return and alert that a new user has joined a group.

.Net Core SignalR - connection timeout - heartbeat timer - connection state change handling

just to be clear up-front, this questions is about .Net Core SignalR, not the previous version.
The new SignalR has an issue with WebSockets behind IIS (I can't get them to work on Chrome/Win7/IIS express). So instead I'm using Server Sent Events (SSE).
However, the problem is that those time out after about 2 minutes, the connection state goes from 2 to 3. Automatic reconnect has been removed (apparently it wasn't working really well anyway in previous versions).
I'd like to implement a heartbeat timer now to stop clients from timing out, a tick every 30 seconds may well do the job.
Update 10 November
I have now managed to implement the server side Heartbeat, essentially taken from Ricardo Peres' https://weblogs.asp.net/ricardoperes/signalr-in-asp-net-core
in startup.cs, add to public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
app.UseSignalR(routes =>
{
routes.MapHub<TheHubClass>("signalr");
});
TimerCallback SignalRHeartBeat = async (x) => {
await serviceProvider.GetService<IHubContext<TheHubClass>>().Clients.All.InvokeAsync("Heartbeat", DateTime.Now); };
var timer = new Timer(SignalRHeartBeat).Change(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(30));
HubClass
For the HubClass, I have added public async Task HeartBeat(DateTime now) => await Clients.All.InvokeAsync("Heartbeat", now);
Obviously, both the timer, the data being sent (I'm just sending a DateTime) and the client method name can be different.
Update .Net Core 2.1+
See the comment below; the timer callback should no longer be used. I've now implemented an IHostedService (or rather the abstract BackgroundService) to do that:
public class HeartBeat : BackgroundService
{
private readonly IHubContext<SignalRHub> _hubContext;
public HeartBeat(IHubContext<SignalRHub> hubContext)
{
_hubContext = hubContext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _hubContext.Clients.All.SendAsync("Heartbeat", DateTime.Now, stoppingToken);
await Task.Delay(30000, stoppingToken);
}
}
}
In your startup class, wire it in after services.AddSignalR();:
services.AddHostedService<HeartBeat>();
Client
var connection = new signalR.HubConnection("/signalr", { transport: signalR.TransportType.ServerSentEvents });
connection.on("Heartbeat", serverTime => { console.log(serverTime); });
Remaining pieces of the initial question
What is left is how to properly reconnect the client, e.g. after IO was suspended (the browser's computer went to sleep, lost connection, changed Wifis or whatever)
I have implemented a client side Heartbeat that is working properly, at least until the connection breaks:
Hub Class: public async Task HeartBeatTock() => await Task.CompletedTask;
Client:
var heartBeatTockTimer;
function sendHeartBeatTock() {
connection.invoke("HeartBeatTock");
}
connection.start().then(args => {
heartBeatTockTimer = setInterval(sendHeartBeatTock, 10000);
});
After the browser suspends IO for example, the invoke method would throw an exception - which cannot be caught by a simple try/catch because it is async.
What I tried to do for my HeartBeatTock was something like (pseudo-code):
function sendHeartBeatTock
try connection.invoke("HeartbeatTock)
catch exception
try connection.stop()
catch exception (and ignore it)
finally
connection = new HubConnection().start()
repeat try connection.invoke("HeartbeatTock")
catch exception
log("restart did not work")
clearInterval(heartBeatTockTimer)
informUserToRefreshBrowser()
Now, this does not work for a few reasons. invoke throws the exception after the code block executes due to being run asynchronous. It looks as though it exposes a .catch() method, but I'm not sure how to implement my thoughts there properly.
The other reason is that starting a new connection would require me to re-implement all server calls like "connection.on("send"...) - which appears silly.
Any hints as to how to properly implement a reconnecting client would be much appreciated.
This is an issue when running SignalR Core behind IIS. IIS will close idle connections after 2 minutes. The long term plan is to add keep alive messages which, as a side effect, will prevent IIS from closing the connection. To work around the problem for now you can:
send periodically a message to the clients
change the idle-timeout setting in IIS as described here
restart the connection on the client side if it gets closed
use a different transport (e.g. long polling since you cannot use webSockets on Win7/Win2008 R2 behind IIS)
I've got a working solution now (tested in Chrome and FF so far). In the hope to either motivate you to come up with something better, or to save you a little while coming up with something like this yourselves, I'm posting my solution here:
The Heartbeat-"Tick" message (the server routinely pinging the clients) is described in the question above.
The client ("Tock" part) now has:
a function to register the connection, so that the callback methods (connection.on()) can be repeated; they'd be lost after just restarting a "new HubConnection" otherwise
a function to register the TockTimer
and a function to actually send Tock pings
The tock method catches errors upon sending, and tries to initiate a new connection. Since the timer keeps running, I'm registering a new connection and then simply sit back and wait for the next invocation.
Putting the client together:
// keeps the connection object
var connection = null;
// stores the ID from SetInterval
var heartBeatTockTimer = 0;
// how often should I "tock" the server
var heartBeatTockTimerSeconds = 10;
// how often should I retry after connection loss?
var maxRetryAttempt = 5;
// the retry should wait less long then the TockTimer, or calls may overlap
var retryWaitSeconds = heartBeatTockTimerSeconds / 2;
// how many retry attempts did we have?
var currentRetryAttempt = 0;
// helper function to wait a few seconds
$.wait = function(miliseconds) {
var defer = $.Deferred();
setTimeout(function() { defer.resolve(); }, miliseconds);
return defer;
};
// first routine start of the connection
registerSignalRConnection();
function registerSignalRConnection() {
++currentRetryAttempt;
if (currentRetryAttempt > maxRetryAttempt) {
console.log("Clearing registerHeartBeatTockTimer");
clearInterval(heartBeatTockTimer);
heartBeatTockTimer = 0;
throw "Retry attempts exceeded.";
}
if (connection !== null) {
console.log("registerSignalRConnection was not null", connection);
connection.stop().catch(err => console.log(err));
}
console.log("Creating new connection");
connection = new signalR.HubConnection("/signalr", { transport: signalR.TransportType.ServerSentEvents });
connection.on("Heartbeat", serverTime => { console.log(serverTime); });
connection.start().then(() => {
console.log("Connection started, starting timer.");
registerHeartBeatTockTimer();
}).catch(exception => {
console.log("Error connecting", exception, connection);
});
}
function registerHeartBeatTockTimer() {
// make sure we're registered only once
if (heartBeatTockTimer !== 0) return;
console.log("Registering registerHeartBeatTockTimer");
if (connection !== null)
heartBeatTockTimer = setInterval(sendHeartBeatTock, heartBeatTockTimerSeconds * 1000);
else
console.log("Connection didn't allow registry");
}
function sendHeartBeatTock() {
console.log("Standard attempt HeartBeatTock");
connection.invoke("HeartBeatTock").then(() => {
console.log("HeartbeatTock worked.") })
.catch(err => {
console.log("HeartbeatTock Standard Error", err);
$.wait(retryWaitSeconds * 1000).then(function() {
console.log("executing attempt #" + currentRetryAttempt.toString());
registerSignalRConnection();
});
console.log("Current retry attempt: ", currentRetryAttempt);
});
}
Client version based on ExternalUse's answer...
import * as signalR from '#aspnet/signalr'
import _ from 'lodash'
var connection = null;
var sendHandlers = [];
var addListener = f => sendHandlers.push(f);
function registerSignalRConnection() {
if (connection !== null) {
connection.stop().catch(err => console.log(err));
}
connection = new signalR.HubConnectionBuilder()
.withUrl('myHub')
.build();
connection.on("Heartbeat", serverTime =>
console.log("Server heartbeat: " + serverTime));
connection.on("Send", data =>
_.each(sendHandlers, value => value(data)));
connection.start()
.catch(exception =>
console.log("Error connecting", exception, connection));
}
registerSignalRConnection();
setInterval(() =>
connection.invoke("HeartBeatTock")
.then(() => console.log("Client heatbeat."))
.catch(err => {
registerSignalRConnection();
}), 10 * 1000);
export { addListener };

Multiple subscribers to the OnSendingHeaders event in an owin middleware

I have two middlewares similar to the ones below which are both subscribing to the OnSendingHeaders event. One sets some headers and the other one removes some headers:
public class Middleware1 : OwinMiddleware
{
public Middleware1(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
context.Response.OnSendingHeaders(state =>
{
var res = (OwinResponse)state;
// im removing some headers
}, context.Response);
await Next.Invoke(context);
}
}
public class Middleware2 : OwinMiddleware
{
public Middleware2(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
context.Response.OnSendingHeaders(state =>
{
var res = (OwinResponse)state;
// im setting some headers
}, context.Response);
await Next.Invoke(context);
}
}
I also have web api controllers that creates excel and pdf-documents and returns an IHttpActionResult. That piece of code is working as intended.
However, when I issue a request to a controller that returns a pdf/excel-document, the response gets aborted in my browser after the headers are sent. The response is sent back as a http status code 200 and the correct Content-Length header is sent, but the file to be downloaded gets aborted.
This error seems to be tied to the subscribing of the OnSendingHeaders event. Because if I remove the middlewares or set/remove the headers without subscribing to the event (calling context.Response.Headers directly), everything works fine.
Is this event not supposed to be subscribed to multiple times or what could otherwise be causing this issue?

Asp.net MVC5 async action result executed, but response not sent till web server shutdown

I have an async action, which is supposed to return a JSON message to the browser after awaiting some task. Though the ActionResult is built and executed successfully(I'm using my own JsonResult, so I confirmed this by stepping into the source), the browser still gets no response(confirmed by Fiddler).
Meanwhile, it works if I'm awaiting for Task.Delay(), and returning a dummy message.
Strangely, if I rebuild my projects with VS2013 while the IIS Express running my website, all the sudden the browser receives the message that was supposed to be sent several minutes ago! I think it's shutting down the web server makes this happen, however I can't figure out how exactly this is happening.
I've been debugging for a day, disabled everything that I thought could have been related, with no luck. So any help about what could be the cause to this strange behavior is welcome. Thanks, here is the code:
[HttpPost]
public async Task<JsonResult> Update(string token)
{
try
{
//This works
//await Task.Delay(TimeSpan.FromSeconds(1));
//return Json(new { error = "testing." });
//This won't work
var feedback = await ServerConnectionKeeper.UpdateStation(token);
return feedback.Success
? Json(new { redirect = Url.Action("Index", "Home") })
: Json(new { error = feedback.Error });
}
catch (Exception ex)
{
return Json(new { error = ex.Message });
}
}
It turns out that I called an async void method, which made some strange unknown(by me) things happen. Here is the some conceptual code:
[HttpPost]
public async Task<JsonResult> Data()
{
await SomeTask();
return Json(new { message = "Testing" });
}
private async Task SomeTask()
{
FireAndForget();
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
}
private async void FireAndForget()
{
await Task.Delay(TimeSpan.FromSeconds(100)).ConfigureAwait(false);
}
With above code, I thought the response would come 1 second after requesting. But it does not, not even 100 seconds after. In some cases I get a InvalidOperationException, in other cases I get nothing.
If I change the async void to async Task, either ConfigureAwait(false) or not, every thing works:
[HttpPost]
public async Task<JsonResult> Data()
{
await SomeTask();
return Json(new { message = "Testing 4" });
}
private async Task SomeTask()
{
FireAndForget();
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
}
private async Task FireAndForget()
{
await Task.Delay(TimeSpan.FromSeconds(100)).ConfigureAwait(false);
}
The reason responses are sent when web server shutting down in my production code, is because my FireAndForget() method there is an async loop reading network messages, and never returns untill the connection closed, and shutting down the web server closes the connection. It's still strange to me that I didn't get InvalidOperationException there though.

Resources