I have a WPF desktop application that consumes Web API over VPN. Relevant details:
And sometimes it throws an uncaught exception from HttpClient on application start:
Error message: Exception of type 'System.OutOfMemoryException' was thrown.
Stack trace:
at MyApp.Net.Services.RestNetClient.<GetMyRoles>d__5.MoveNext()
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine& stateMachine)
at MyApp.Net.Services.RestNetClient.GetMyRoles()
<...>
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at MyApp.Wpf.UI.App.<OnStartup>d__3.MoveNext() in <...>\App.xaml.cs:line 44
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
here is relevant part of RestNetClient
class implementation
public class RestNetClient : INetClient {
public RestNetClient(IRemoteConfig config) {
if (Client == null) {
HttpClientHandler authHandler = new HttpClientHandler {
Credentials = CredentialCache.DefaultNetworkCredentials
};
Client = new HttpClient(authHandler) { BaseAddress = config.ServerAddress };
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
protected static HttpClient Client { get; private set; }
public async Task<IServiceOperationResult<AppRole>> GetMyRoles() {
var result = new ServiceOperationResult<AppRole>();
try {
HttpResponseMessage response = await Client.GetAsync("api/ntds/getmyroles");
if (response.IsSuccessStatusCode) {
String json = await response.Content.ReadAsStringAsync();
result.Value = JsonConvert.DeserializeObject<AppRole>(json);
} else {
await response.SetErrorCode(result);
}
} catch (Exception ex) {
result.HResult = ErrorCode.E_INVALIDARG;
result.ErrorMessage = ex.Message;
}
return result;
}
<...>
The exception isn't caught in GetMyRoles
method's catch
clause and I get it in WPF's App.Dispatch.UnhandledException
event handler. I'm calling this method from App.OnStartup
method, like this:
protected override async void OnStartup(StartupEventArgs e) {
<...>
var result = await restClient.GetMyroles();
<...>
}
I know that async void
isn't the proper way to call async tasks, but I have to do some on startup, but my problem is with strange OutOfMemoryException
thrown by HttpClient
. If it does matter, return value of API call is a 4 byte integer, so I'm not trying to download 8K blue-rays and there is plenty of memory on a client.
Key observations:
finally, I was able to find and fix issue. Actual problem was here:
Credentials = CredentialCache.DefaultNetworkCredentials
It appears that HttpClient failed to read credential cache content (no idea on this). And fix was to replace that line with:
UseDefaultCredentials = true
and everything started to work correctly.