-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use HttpClient.Send instead of AsyncHelpers.RunSync() #2160
Comments
Is there a problem with current implementation? I am 99.9% sure that HttpClient sync methods are just wrappers over async API. Making RestSharp use .NET 5+ code requires more pragma #if's and there are already too many of those. If I understand if it actually makes sense, and it works better compared with the current implementation, I'd be ready to make a change. |
It was my general understanding that async code needs to "spread" through the calling stack and calling the async method synchronously is bad practice. We have this as a coding standard and we won't be using RestSharp for cases like this. If HttpClient is calling async code synchronously, then it would also be concerning, but less so because Microsoft would be managing it and maybe making it true synchronous one day too. We simply want to save ourselves from hard-to-debug / not-replicable issues that this can cause. |
I am not sure. It seems like loads of work. Because it starts with a copy-paste implementation of
I am not sure how using protected internal sealed override HttpResponseMessage Send(HttpRequestMessage request,
CancellationToken cancellationToken)
{
ValueTask<HttpResponseMessage> sendTask = SendAsync(request, async: false, cancellationToken);
return sendTask.IsCompleted ?
sendTask.Result :
sendTask.AsTask().GetAwaiter().GetResult();
} It is way less comprehensive compared to RestSharp async wrapper, which does a lot more than getting an awaiter and then getting it's result. |
I think what you sharing is not running async, and the call stack is not awaited. response = async ?
await base.SendAsync(request, cts.Token).ConfigureAwait(false) :
base.Send(request, cts.Token); if (async)
{
await response.Content.LoadIntoBufferAsync(_maxResponseContentBufferSize, cts.Token).ConfigureAwait(false);
}
else
{
response.Content.LoadIntoBuffer(_maxResponseContentBufferSize, cts.Token);
} This makes me believe that while method signatures are async, the running code never awaits anything, staying on the same thread and is synchronous. |
Can you link to the file with the above code? What I get from source link isn't the same. The .NET 8 code works like this: And the last one simply does |
The above examples were from .NET 5.
// Wait for the send request to complete, getting back the response.
response = async ?
await base.SendAsync(request, cts.Token).ConfigureAwait(false) :
base.Send(request, cts.Token);
if (response == null)
{
throw new InvalidOperationException(SR.net_http_handler_noresponse);
} Searching online a bit, I found a Different place that uses similar code at But here is where I end up by browsing using ReSharper. the method starts at line 658, namespace private async ValueTask<HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption,
bool async, bool emitTelemetryStartStop, CancellationToken cancellationToken)
{
// Combines given cancellationToken with the global one and the timeout.
CancellationTokenSource cts = PrepareCancellationTokenSource(cancellationToken, out bool disposeCts, out long timeoutTime);
bool buffered = completionOption == HttpCompletionOption.ResponseContentRead &&
!string.Equals(request.Method.Method, "HEAD", StringComparison.OrdinalIgnoreCase);
bool telemetryStarted = false, responseContentTelemetryStarted = false;
if (HttpTelemetry.Log.IsEnabled())
{
if (emitTelemetryStartStop && request.RequestUri != null)
{
HttpTelemetry.Log.RequestStart(request);
telemetryStarted = true;
}
}
// Initiate the send.
HttpResponseMessage? response = null;
try
{
// Wait for the send request to complete, getting back the response.
response = async ?
await base.SendAsync(request, cts.Token).ConfigureAwait(false) :
base.Send(request, cts.Token);
if (response == null)
{
throw new InvalidOperationException(SR.net_http_handler_noresponse);
}
// Buffer the response content if we've been asked to and we have a Content to buffer.
if (buffered && response.Content != null)
{
if (HttpTelemetry.Log.IsEnabled() && telemetryStarted)
{
HttpTelemetry.Log.ResponseContentStart();
responseContentTelemetryStarted = true;
}
if (async)
{
await response.Content.LoadIntoBufferAsync(_maxResponseContentBufferSize, cts.Token).ConfigureAwait(false);
}
else
{
response.Content.LoadIntoBuffer(_maxResponseContentBufferSize, cts.Token);
}
}
if (NetEventSource.Log.IsEnabled()) NetEventSource.ClientSendCompleted(this, response, request);
return response;
}
catch (Exception e)
{
LogRequestFailed(telemetryStarted);
response?.Dispose();
if (e is OperationCanceledException operationException && TimeoutFired(cancellationToken, timeoutTime))
{
HandleSendTimeout(operationException);
throw CreateTimeoutException(operationException);
}
HandleFinishSendAsyncError(e, cts);
throw;
}
finally
{
if (HttpTelemetry.Log.IsEnabled() && telemetryStarted)
{
if (responseContentTelemetryStarted)
{
HttpTelemetry.Log.ResponseContentStop();
}
HttpTelemetry.Log.RequestStop();
}
HandleFinishSendCleanup(cts, disposeCts);
}
} |
This one is just a wrapper, it does nothing more than adding telemetry. I have looked at the one that actually does the work, although it's not easy to find out what is called where because of interfaces and, therefore, implicit resolution. Still, certainly, in latest .NET that's |
No interfaces, and the flow is very straightforward in the above example (again, NET 5). |
I am not looking at .NET 5, it's out of support for two years now.
It goes through here That's exactly what I wrote before. |
I am a little lost now... This issue is requesting to use |
What I mean is that I don't see any advantage of using |
Also, using async helper allows us to easily create extensions for sync calls by combining the helper with async functions. For using |
I have noticed that the synchronous
Execute
methods are just wrapping nativeHttpClient.SendAsync
,public RestResponse Execute(RestRequest request) => AsyncHelpers.RunSync(() => ExecuteAsync(request));
I was wondering if there already is an implementation or if there is a plan to use the native
HttpClient.Send
, which was only introduced in NET 5 and onwards?A some related discussion is here:
https://stackoverflow.com/questions/53529061/whats-the-right-way-to-use-httpclient-synchronously
The text was updated successfully, but these errors were encountered: