I have written an asynchronous HttpModule which logs all the request coming to a website.
When the request arrives at the website, the custom http module calls the WebAPI to log the information to the database. .Net 4.5/Visual studio with IIS Express.
////////////Custom HTTP module////////////////////
public class MyHttpLogger : IHttpModule
{
public void Init(HttpApplication httpApplication)
{
EventHandlerTaskAsyncHelper taskAsyncHelper = new EventHandlerTaskAsyncHelper(LogMessage);
httpApplication.AddOnBeginRequestAsync(taskAsyncHelper.BeginEventHandler, taskAsyncHelper.EndEventHandler);
}
private async Task LogMessage(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
var ctx = app.Context;
//use default credentials aka Windows Credentials
HttpClientHandler handler = new HttpClientHandler()
{
UseDefaultCredentials = true
};
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri("http://localhost:58836/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var actvity = new Activities() { AppId = "TEST123", ActivityId = 10, CreatedBy = 1 };
await client.PostAsJsonAsync("api/activity", actvity);
}
}
Simplified WebAPI code for “api/activity”.
public async Task<HttpResponseMessage>Post(Activities activity)
{
await Task.Delay(30000);
// Database logging code goes here….
return new HttpResponseMessage(HttpStatusCode.Created);
}
The question is: when the PostAsJsonAsync is getting executed the code stops there. This is correct as the code has to wait. But the calling routine is not getting continued asynchronously as expected and the website response is slower by 30 seconds.
What is wrong with this code? Hooking an async HttpModule should not interfere in the flow of the Http application? Currently when I visit the website the code is getting blocked.
Now, you have to understand the difference between HTTP request and local application. HTTP is totally stateless. Basically you send out a request, the server process it and send back the response. The server does NOT keep any state info about the client, it doesn't have any idea who the client is at all. So in your case, browser sends out a request -> server got it, waited for 30 sec to process it and then sends back result -> browser got the response and this is the point where the browser shows the result. I am assuming what you are trying to do is, browser sends a request, and then you expect the browser to display something and then after 30 sec the server finishes processing and you want to display something else. This is NOT possible to do with await because of the reason mentioned above. What you should do is to write some javascript codes to send out the request with some identifier, and then ask the server every x seconds to see if task{ID} has finished, if so whats the result.
Related
I've a Blazor client which shows a live stream of images coming from a ASP.NET Core (.NET 6) Web Application Server which in turn requests the stream to a camera (multiple cameras actually), which returns a multipart/x-mixed-replace content.
When the user (client side) leaves the page containing the stream of images, I'd like to server to stop requesting the camera live stream. I know I need to use Cancellation Token, as explained here, but it seems not to work if client side I make the stream request like this:
// CameraLive.razor
<img src="CameraSystem/startStreaming">
Server side, request is forwarded to the actual device like this (simplified code)
// CameraSystemController.cs
[ApiController]
[Route("[controller]")]
public class CameraSystemController : ControllerBase
{
private string _contentTypeStreaming = "multipart/x-mixed-replace;boundary=myboundary";
private HttpClient _httpClient;
[HttpGet("startStreaming")]
public async Task<IActionResult>StartCameraStreaming(CancellationToken token)
{
Stream stream = await _httpClient.GetStreamAsync("http://[...]", token);
FileStreamResult result = new FileStreamResult(stream, _contentTypeStreaming) {
EnableRangeProcessing = true
};
return result;
}
}
Currently, every time a client open CameraLive.razor page, the server request a stream to the camera and returns it. If on the same client I move to another page of the web application and later I re-enter CameraLive.razor, I see the previous request is not canceled and a new one is made. Actually, if I look at task manager, I can see the bandwidth usage is doubled.
The requests are canceled only if I close the browser tab.
I'd like the request to stop as soon as the client moves to another page, as it happens if the client was to be designed this way:
// CameraLive.razor
<img src="#_frame">
// CameraLive.razor.cs
private CancellationTokenSource _tokenSource;
private System.IO.Stream _stream;
private void RetrieveLiveStream() {
_stream = await Http.GetStreamAsync("CameraSystem/startStreaming", _tokenSource.Token);
// Every time a new piece of stream comes,
// I need to extract the frame from it and update the web page
_frame = ...
stateHasChanged();
}
public void Dispose()
{
if (_tokenSource != null) {
_tokenSource.Cancel();
_tokenSource.Dispose();
}
}
If I design the client in this second way the cancellation works: as soon as the user moves to another page, the request is canceled (I can see it clearly from the bandwidth usage data)
However I'd have liked to stick with the first solution because
It's simpler, I don't have to handle all the frame-extraction related code, and I suppose the way browser does it is more efficient than the way I would do (I've never done it, I just tried making the request, doing nothing with the result and then leave the page just to make sure that request is actually canceled).
I have the feeling that having the browser auto-update the image is more efficient than having me calling StateHasChanged() continuously.
So, from what I get, in order to have the cancellationToken to work properly, I need to send the token from the client along with the http request. However in the first solution it seems like it is not possible to pass the cancellationToken along with the request URL, so I don't know what I should do.
Any advice?
I need to send few requests to specific URL every 5 minutes from web service methods. I tried using HttpClient, HttpWebRequest and RestClient, but all sockets are exhausted after hour or two.
Can you suggest me what is the right way to send few requests to some URL every 5 minutes without all sockets to be exhausted? Thank you in advance for any help.
Sample of the code
private static HttpClient client = new HttpClient();
public async Task sendReq()
{
client.DefaultRequestHeaders.ConnectionClose = true;
var res = await client.GetStringAsync(url);
}
my application is using Azure Service Bus to store messages. I have an Azure function called HttpTriggerEnqueuewhich allow me to enqueue messages. The problem is that this function can be invoked hundreds times in a little interval of time. When I call the HttpTriggerEnqueue once, twice, 10 times, or 50 times everything works correctly. But when I call it 200, 300 times (which is my use case) I get an error and not all messages are enqueued. From the functions portal I get the following error.
threshold exceeded [connections]
I tried both the .NET sdk and the HTTP request. Here is my code
HTTP REQUEST:
try
{
var ENQUEUE = "https://<MyNamespace>.servicebus.windows.net/<MyEntityPath>/messages";
var client = new HttpClient(new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip });
var request = new HttpRequestMessage(HttpMethod.Post, ENQUEUE);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var sasToken = SASTokenGenerator.GetSASToken(
"https://<MyNamespace>.servicebus.windows.net/<MyEntityPath>/",
"<MyKeyName>",
"<MyPrimaryKey>",
TimeSpan.FromDays(1)
);
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", sasToken);
request.Content = new StringContent(message, Encoding.UTF8, "application/json");
request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
var res = await client.SendAsync(request);
}
catch (Exception e) { }
And the code using the SDK:
var qClient = QueueClient.CreateFromConnectionString(MyConnectionString, MyQueueName);
var bMessage = new BrokeredMessage(message);
qClient.Send(bMessage);
qClient.Close();
I have the standard tier pricing on Azure.
If I call the function 300 (for example) times in a little interval of time I get the error. How can I solve?
The actual issue here isn't with the Service Bus binding (although you should follow the advice that #Mikhail gave for that), it's a well known issue with HttpClient. You shouldn't be re-creating the HttpClient on every function invocation. Store it in a static field so that it can be reused. Read this blog post for a great breakdown on the actual issue. The main point being that unless you refactor this to use a single instance of HttpClient you're going to continue to run into port exhaustion.
From the MSDN Docs:
HttpClient is intended to be instantiated once and re-used throughout the life of an application. Especially in server applications, creating a new HttpClient instance for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors.
You should use Service Bus output binding to send messages to Service Bus from Azure Function. It will handle connection management for you, so you shouldn't be getting such errors.
I'm trying to create an application which will continously send many http requests to one web endpoint which uses NTLM authentification (via login and password). To optimize the app, it was decided to use multithread execution, so I can send many http requests simultaneously. I'm using following code:
private string DoGetRequestWithCredentials(Uri callUri, NetworkCredential credentials)
{
using (var handler = new HttpClientHandler {Credentials = credentials})
{
using (var client = new HttpClient(handler))
{
MediaTypeWithQualityHeaderValue mtqhv;
MediaTypeWithQualityHeaderValue.TryParse(
"application/json;odata=verbose", out mtqhv);//success
client.DefaultRequestHeaders.Accept.Add(mtqhv);
client.Timeout = RequestTimeout.Value;
var result = client.GetAsync(callUri).Result;
result.EnsureSuccessStatusCode();
return result.Content.ReadAsStringAsync().Result;
}
}
}
Code works fine in a single thread, but when I enable multithread, I'm starting to receive 401 UNAUTHORIZED exceptions.
My suggestion why this happens is because some of requests in multithread are trying to be executed between consiquential NTLM calls.
Is my assumption correct?
How can I avoid this situations without locking the method? Because I really want requests to be sent simultaneously
Given an async controller:
public class MyController : AsyncController
{
[NoAsyncTimeout]
public void MyActionAsync() { ... }
public void MyActionCompleted() { ... }
}
Assume MyActionAsync kicks off a process that takes several minutes. If the user now goes to the MyAction action, the browser will wait with the connection open. If the user closes his browser, the connection is closed. Is it possible to detect when that happens on the server (preferably inside the controller)? If so, how? I've tried overriding OnException but that never fires in this scenario.
Note: I do appreciate the helpful answers below, but the key aspect of this question is that I'm using an AsyncController. This means that the HTTP requests are still open (they are long-lived like COMET or BOSH) which means it's a live socket connection. Why can't the server be notified when this live connection is terminated (i.e. "connection reset by peer", the TCP RST packet)?
I realise this question is old, but it turned up frequently in my search for the same answer.
The details below only apply to .Net 4.5
HttpContext.Response.ClientDisconnectedToken is what you want. That will give you a CancellationToken you can pass to your async/await calls.
public async Task<ActionResult> Index()
{
//The Connected Client 'manages' this token.
//HttpContext.Response.ClientDisconnectedToken.IsCancellationRequested will be set to true if the client disconnects
try
{
using (var client = new System.Net.Http.HttpClient())
{
var url = "http://google.com";
var html = await client.GetAsync(url, HttpContext.Response.ClientDisconnectedToken);
}
}
catch (TaskCanceledException e)
{
//The Client has gone
//you can handle this and the request will keep on being processed, but no one is there to see the resonse
}
return View();
}
You can test the snippet above by putting a breakpoint at the start of the function then closing your browser window.
And another snippet, not directly related to your question but useful all the same...
You can also put a hard limit on the amount of time an action can execute for by using the AsyncTimeout attribute. To use this use add an additional parameter of type CancellationToken. This token will allow ASP.Net to time-out the request if execution takes too long.
[AsyncTimeout(500)] //500ms
public async Task<ActionResult> Index(CancellationToken cancel)
{
//ASP.Net manages the cancel token.
//cancel.IsCancellationRequested will be set to true after 500ms
try
{
using (var client = new System.Net.Http.HttpClient())
{
var url = "http://google.com";
var html = await client.GetAsync(url, cancel);
}
}
catch (TaskCanceledException e)
{
//ASP.Net has killed the request
//Yellow Screen Of Death with System.TimeoutException
//the return View() below wont render
}
return View();
}
You can test this one by putting a breakpoint at the start of the function (thus making the request take more than 500ms when the breakpoint is hit) then letting it run out.
Does not Response.IsClientConnected work fairly well for this? I have just now tried out to in my case cancel large file uploads. By that I mean if a client abort their (in my case Ajax) requests I can see that in my Action. I am not saying it is 100% accurate but my small scale testing shows that the client browser aborts the request, and that the Action gets the correct response from IsClientConnected.
It's just as #Darin says. HTTP is a stateless protocol which means that there are no way (by using HTTP) to detect if the client is still there or not. HTTP 1.0 closes the socket after each request, while HTTP/1.1 can keep it open for a while (a keep alive timeout can be set as a header). That a HTTP/1.1 client closes the socket (or the server for that matter) doesn't mean that the client has gone away, just that the socket hasn't been used for a while.
There are something called COMET servers which are used to let client/server continue to "chat" over HTTP. Search for comet here at SO or on the net, there are several implementations available.
For obvious reasons the server cannot be notified that the client has closed his browser. Or that he went to the toilet :-) What you could do is have the client continuously poll the server with AJAX requests at regular interval (window.setInterval) and if the server detects that it is no longer polled it means the client is no longer there.