4

I have what seems a really weird situation. I have some async/await code which uses RestSharp to get some data off several rest APIs (same API hosted on different URLs) which a kind of directory service returns.

Now the problem: one of the APIs returned by said directory service is somehow "private" and initiating a SSL connection to it fails. Fiddler captures the following response:

HTTP/1.1 200 Connection Established
FiddlerGateway: Direct
StartTime: 19:13:11.117
Connection: close

fiddler.network.https> HTTPS handshake to foo.bar.com failed. System.IO.IOException The handshake failed due to an unexpected packet format.

I would ideally like to skip getting data from this API in case this happens and just move on. However, it turns out that a try/catch doesn't help! A NullReferenceException is thrown but even a generic try/catch doesn't manage to catch it.

The code is pretty straightforward:

try
{
   await GetDataAsync(url);
}
catch 
{
   // never gets called
}

and

private async Task<List<Data>> GetDataAsync(string url)
{
   var request = new RestRequest("/foo");
   var restClient = new RestClient(url);

   var response = await restClient.ExecuteTaskAsync<List<Data>>(request); // <-- this throws 
   return response.Data;
}

I've extracted the code in a library and tried it both in a Console app and in a WPF app, same result, catch block never gets entered.

Any ideas?

l.e.: as requested, here's the full exception stack trace

Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
at RestSharp.RestClient.<>c__DisplayClass15`1.<ExecuteTaskAsync>b__12(IRestResponse`1 response, RestRequestAsyncHandle _)
at RestSharp.RestClient.DeserializeResponse[T](IRestRequest request, Action`2 callback, IRestResponse response, RestRequestAsyncHandle asyncHandle)
at RestSharp.RestClient.<>c__DisplayClassa`1.<ExecuteAsync>b__9(IRestResponse response, RestRequestAsyncHandle asyncHandle)
at RestSharp.RestClient.ProcessResponse(IRestRequest request, HttpResponse httpResponse, RestRequestAsyncHandle asyncHandle, Action`2 callback)
at RestSharp.RestClient.<>c__DisplayClass3.<ExecuteAsync>b__0(HttpResponse r)
at RestSharp.Http.ExecuteCallback(HttpResponse response, Action`1 callback)
at RestSharp.Http.ResponseCallback(IAsyncResult result, Action`1 callback)
at RestSharp.Http.<>c__DisplayClass3.<GetStyleMethodInternalAsync>b__1(IAsyncResult result)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.ContextAwareResult.CompleteCallback(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.HttpWebRequest.SetResponse(Exception E)
at System.Net.HttpWebRequest.CheckWriteSideResponseProcessing()
at System.Net.ConnectStream.ProcessWriteCallDone(ConnectionReturnResult returnResult)
at System.Net.HttpWebRequest.WriteCallDone(ConnectStream stream, ConnectionReturnResult returnResult)
at System.Net.ConnectStream.CallDone(ConnectionReturnResult returnResult)
at System.Net.ConnectStream.IOError(Exception exception, Boolean willThrow)
at System.Net.ConnectStream.HandleWriteHeadersException(Exception e, WebExceptionStatus error)
at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.TlsStream.ResumeIOWorker(Object result)
at System.Net.TlsStream.WakeupPendingIO(IAsyncResult ar)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Security.SslState.FinishHandshake(Exception e, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
at System.Net.AsyncProtocolRequest.CompleteRequest(Int32 result)
at System.Net.FixedSizeReader.CheckCompletionBeforeNextRead(Int32 bytes)
at System.Net.FixedSizeReader.ReadCallback(IAsyncResult transportResult)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.ContextAwareResult.CompleteCallback(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
svick
  • 236,525
  • 50
  • 385
  • 514
adrian h.
  • 2,996
  • 3
  • 18
  • 24
  • put a breakpoint to `return response.Data;` and see the content of `response`. – L.B Oct 06 '14 at 17:26
  • I don't get to that line, it throws on the ExecuteTaskAsync. I edited the question to show the line which throws. – adrian h. Oct 06 '14 at 17:29
  • Could you add the exception's stack trace? – i3arnon Oct 06 '14 at 18:47
  • Done,edited original post – adrian h. Oct 06 '14 at 19:15
  • If the exception is not being caught by that try-catch, where is it being caught? – Paulo Morgado Oct 06 '14 at 21:00
  • Nowhere, that's the thing, it bubbles up and eventually crashes the app. I even added a try/catch in the main method, still doesn't catch it. Weirdness! – adrian h. Oct 06 '14 at 21:16
  • Try to avoid the generic version and see what you come up with: `var response = await restClient.ExecuteTaskAsync(request); var list = JsonConvert.DeserializeObject>(response.Content);` (use the appropriate deserializer). – Patrice Gahide Oct 06 '14 at 21:48
  • @adrianhara So, did you try it? Your code is perfectly right, that's why I suspect something's wrong with their implementation of async methods. Maybe they have not payed enough attention when they translated their synchronous code. Eliminating one level of abstraction by getting rid of the generic parameter is IMHO a first step on the path to a possible solution. – Patrice Gahide Oct 07 '14 at 09:25
  • @PatriceGahide sorry didn't get a chance to try it, but basically you were right, it was a bug in RestSharp – adrian h. Oct 07 '14 at 20:52

1 Answers1

2

This seems to be a bug in RestSharp. I'm not sure you can do anything about it without modifying RestSharp's source.

svick
  • 236,525
  • 50
  • 385
  • 514
  • You're right. I also found https://github.com/restsharp/RestSharp/issues/447 which is basically the same issue. HOWEVER, upon getting the latest RestSharp code from GitHub in order to understand why the exception brings down the AppDomain I see that it's now fixed! I stepped through the deserialize code and now it correctly checks if there was an exception and no longer tries to deserialize the response body. – adrian h. Oct 07 '14 at 20:41
  • I now checked out an older commit of RestSharp where the bug was still in to try to figure out why the exception brought down the app. Not really sure, but as far as I can tell it seems that the async methods in RestSharp are wrapping non-async methods on WebRequest (e.g. BeginWebRequest). I'm guessing that exceptions thrown in such scenarios are not supported by the compiler rewriting of async/await code? (and just to be clear, also the current version of RestSharp4 compiles against .net4 and still doesn't use GetResponseAsync(), but since the actual bug is fixed the NRE is no longer thrown) – adrian h. Oct 07 '14 at 20:58