How to get time to first byte with HttpClient class c# - c#

Here's the code I am currently using to make calls to the api, where api is a string passed into the function. Set up of the httpClient is done beforehand outside of the method.
HttpResponseMessage response = await httpClient.GetAsync(api);
String strResp = await response.Content.ReadAsStringAsync();
httpClient.DefaultRequestHeaders.Accept.Clear();
Is there a way I can track metrics like time to first byte (TTFB) using this code, or will I have to go another route?

You should be able to use the HttpClient.GetAsync(string requestUri, HttpCompletionOption completionOption) method with HttpCompleteOption.ResponseHeadersRead. This should return the HttpResponseMessage as soon as the header is read (not exactly TTFB - "Time to first byte", but TTHR - "Time till header read")
Then you could measure this time using stopwached:
sw = new Stopwatch();
sw.Start();
HttpResponseMessage response = await httpClient.GetAsync(api);
sw.Stop();
Console.WriteLine("TTFB: " + sw.EleapsedMilliseconds)
String strResp = await response.Content.ReadAsStringAsync();
But this is also very unreliable due to the parallel nature of this code.
In this post someone advices to use WebClientand its DownloadProgressChanged in combination with a 'StopWatch'.

Ended up using FiddlerCore by Telerik to get TTFB and loads of other useful metrics.

Related

How to make this method to be async despite of not supporting the 'await' keyword

I'm trying to make a method that runs as an async method. The problem is the longest part is not 'awaitable' (not support the 'await' keyword ). I based my code using this link: here.
Some parts in my code have taken from Postman - HTTP POST to an API.
private async Task<byte[]> PostRequestToOptiRoutesAsync()
{
string urlCallBack = u.Trim(this.URLAchzraR4M);
var client = new RestClient(u.Trim(PrmtrimGlobliim_407_2.Mchrozt));
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("hfd_xml", u.Trim(BlovXMLBkshaOPT).Replace("\r", "").Replace("\n", "").Replace("\t", ""));
request.AddParameter("link", urlCallBack);
//UNTIL HERE - IT IS SYNC CODE AND NOT RELEVANT
//FROM HERE - THE POST TO THE API
IRestResponse response = client.Execute(request); // client.Execute is not awaitable - THE PROBLEMATIC ROW IS HERE
return u.UTF8FromAnsi(response.Content); // u.UTF8FromAnsi CONVERT THE RESULT- IGNORE IT
}
private async Task GetOptimalRoute()
{
Task<byte[]> result = PostRequestToOptiRoutesAsync();
await result;
}
The problem is that when I call GetOptimalRoute() using VS debug, my thread is not immediately moved to the next row, but it acts like sync code. It pauses for few seconds - unlike the code in the link above!!
How can I resolve this issue and make this code to be async?

Best practice for making a HTTP Post from asp.net Page_Load?

Seems like simple task, but the interwebs are flooded with different ways to approach this, so what is best practice for making a simple HTTP POST from asp.net Page_Load (I'm on .net 4.7) ?
Many resources point to the fact that HttpClient.PostAsync is the most lightweight approach.
However, the example here: https://learn.microsoft.com/en-us/aspnet/web-forms/overview/performance-and-caching/using-asynchronous-methods-in-aspnet-45 - uses WebClient.
Also, both of theses approaches require setting Page Async="true" on the page - I am seeing conflicting information on whether this is actually the right approach.
Context: My page looks like it is spending a lot of time in BLOCKED_TIME during two requests that are made in Page_Load.
I suspect this is due to the way I currently make the POST:
public string makePost(string parametersToPassOn, string command)
{
HttpContent c = new StringContent(parametersToPassOn, Encoding.UTF8, "application/json");
var t = Task.Run(() => fireRESTcall(new Uri(walletProxyURL + command), c));
t.Wait();
return t.Result;
}
static async Task<String> fireRESTcall(Uri u, HttpContent c)
{
var response = string.Empty;
using (var client = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = u,
Content = c
};
HttpResponseMessage result = await client.SendAsync(request);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
}
return response;
}
Any help/thoughts would be greatly appreciated.
During the time your page makes an outgoing HTTP request, it can't do anything useful other than waiting for the response. See also Understanding BLOCKED_TIME in PerfView
You probably can't do anything about this delay, as you need the external API for some data, and you need to wait for it to complete in order to do something useful with that data.
So it's advisable to use async/await all the way, using RegisterAsyncTask():
void Page_Load()
{
RegisterAsyncTask(new PageAsyncTask(CallApiAsync));
Page.ExecuteRegisteredAsyncTasks();
}
async Task CallApiAsync() // calls
async Task<string> MakePost() // calls
async Task<String> FireRESTcall()
This frees up the thread to handle a new incoming request, until the outgoing request is finished and the incoming request continues on the same or another thread pool thread.
For this to work with WebForms, you do need Async="true" on your page.

System.Net.Http.HttpClient not timing out after set time

I'm using HttpClient to handle web requests. I'll perform the request like so...
var response = await httpClient.PostAsync(Uri, stringContent);
The timeout is set in the http client setup and I then call it in the class with IHttpClientFactory dependency.
// Setup HttpClient
services.AddHttpClient("ApiName", client =>
{
client.BaseAddress = new Uri("BaseUri");
client.Timeout = new TimeSpan(0, 0, 8);
});
I've inspected httpClient before it runs to see if its using the correct API and it is, the timeout is set to 8 seconds. However, the request takes around a good 1-2 minutes to timeout. I'm not sure why this is happening.
I've tried using a CancellationToken like so...
var cts = new CancellationTokenSource();
cts.CancelAfter(8000);
var response = await httpClient.PostAsync(Uri, stringContent, cts.Token);
and waiting for the exception to occur but it still takes 1-2 minutes.
Based on the HttpClient.Timeout documentation, a DNS query may take up to 15 seconds to return or time out. However that does not explain the 1-2 minute delay you are seeing.
Have you looked at "Better timeout handling with HttpClient"? I don't know if it will work, but rather than using PostAsync you might try building the POST with HttpRequestMessage and use SendAsync instead, similar to the example in the article:
var request = new HttpRequestMessage(HttpMethod.Post, "http://foo/");
request.Content = stringContent;
request.SetTimeout(TimeSpan.FromSeconds(8)); // Uses HttpRequestExtensions from article
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(8));
var response = await client.SendAsync(request, cts.Token);

Transform Restsharp call to asynchronous

I've this function :
public string GetSecurityCrumb()
{
string uri = $"{Url}/crumbIssuer/api/xml";// URL where the XML is available
var client = new RestClient(uri); // define it as the actual client
var request = new RestRequest(Method.GET);
byte[] ua = Encoding.ASCII.GetBytes(Username + ":" + ApiToken); // Encoding username and token in base 64
request.AddHeader("authorization", "Basic " + Convert.ToBase64String(ua));// adding header to get the xml
IRestResponse response = client.Execute(request);
XDocument document = XDocument.Parse(response.Content);// parsing the content of the response in a document
var crumb = document.Root.Element("crumb").Value;// retrieve the content of the crumb only
return crumb;
}
I tried a lot of things to do this aynchronous, but I just don't see how I can return string value if I change my Rest call to an aynschronous one.
Maybe somebody already got this kind of problem and could help me.
EDIT 1
I tried this :
public async Task<string> GetSecurityCrumb()
{
string uri = $"{Url}/crumbIssuer/api/xml";// URL where the XML is available
var client = new RestClient(uri); // define it as the actual client
var request = new RestRequest(Method.GET);
byte[] ua = Encoding.ASCII.GetBytes(Username + ":" + ApiToken); // Encoding username and token in base 64
request.AddHeader("authorization", "Basic " + Convert.ToBase64String(ua));// adding header to get the xml
IRestResponse response = await client.ExecuteTaskAsync(request);
XDocument document = XDocument.Parse(response.Content);// parsing the content of the response in a document
var crumb = document.Root.Element("crumb").Value;// retrieve the content of the crumb only
return crumb;
}
but it seems like I need to put await before all my calls on this method and use GetSecurityCrumb().Result to get the real content. I don't know if it's the best way because I've totally 0 error handlers at the moment. A lot of my methods depend of this one so I prefer having the best solution
it seems like I need to put await before all my calls on this method and use GetSecurityCrumb().Result to get the real content
You shouldn't call .Result - that would block on the asynchronous method, removing all the benefits of asynchronous code in the first place.
It's normal to await the task returned from GetSecurityCrumb(). And using that await means that the calling method should also be marked async and return a task. Which means that its callers should use await, so they should be made async, etc. This is all perfectly normal and is the correct way to use async/await.

HttpClient.PostAsJsonAsync never sees when the post is succeeding and responding

We are using an HttpClient to post json to a restful web service. In one instance, we are running into something that has us baffled. Using tools like postman, fiddler etc, we can post to an endpoint and see that it is working. When we do the same with HttpClient.PostAsJsonAsync, we can verify in the software we are posting to that it received the data just fine. However, our PostAsJsonAsync will always eventually time out rather than give us a response.
We have worked with the team that created the service we are consuming, plus our additional testing on our side, and we have not yet been able to truly time out that service.
Every time we do a post with HttpClient, we then can verify that the target software we post to does indeed get the data. Any time we do a post to that target software from any other tool, we always very quickly see a response with status code of 200. Something about HttpClient is failing to accept the response from this particular service. Does anyone have an idea what we can look at from here?
Here's the code (though it is so cookie cutter that I hardly feel it is needed)
public string PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = client.PostAsJsonAsync(useUrl, o).Result;
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return response.Content.ReadAsStringAsync().Result;
}
return "";
}
}
This:
var response = client.PostAsJsonAsync(useUrl, o).Result;
Is causing you code to deadlock. This is often the case when blocking on async API's, and that's why you're experiencing the "I don't see any response coming back" effect.
How is this causing a deadlock? The fact that you are executing this request in an environment that contains a synchronization context, perhaps one which belongs to the UI. It's executing the async request, and when the response arrives, it continues via an IO completion thread which attempts to post the continuation onto that same UI context, which is currently blocked by your .Result call.
If you want to make an HTTP request synchronously, use WebClient instead. If you want to take advantage of the async API properly, then await instead of block with .Result.
I had the same issue and this SO answer fixed it for me.
In a nutshell, you have to use the ConfigureAwait(false) extension to avoid the deadlock:
var response = await client.PostAsJsonAsync(useUrl, o).ConfigureAwait(false);
Is there a reason why you're not following the async await pattern? You're calling an async method, but not awaiting it. You didn't say if the code calling your REST service was a Windows Forms or ASP.NET application, but that .Result is probably causing you issues.
Can you restructure your method like this:
public async Task<string> PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = await client.PostAsJsonAsync(useUrl, o);
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return await response.Content.ReadAsStringAsync();
}
return "";
}
}
This is a slight modification to #Justin Helgerson's solution. There are 2 blocking .Result calls in your method; once you go async you should fix them both.
public async Task<string> PostDataAsync(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = await client.PostAsJsonAsync(useUrl, o);
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return await response.Content.ReadAsStringAsync();
}
return "";
}
}
Note I've also renamed the method to PostDataAsync in accordance with the TAP pattern.
System.Net.ServicePointManager.Expect100Continue = false;
That one line of code in our case fixed the problem. A developer from a different team offered that suggestion, and it works. I have yet to google it and read up on it enough to offer an explanation of what that is addressing.

Categories

Resources