Xamarin Form - TaskCanceledException thrown on await client.PostAsync() - xamarin

Very strange problem (all code is in PCL).
When I call await client.PostAsync() against a webservice (API's), I get the TaskCanceledException.
Few things to note:
This is running fine on Android devices. It only happens when I run the app on iOS, but...
... to make it even stranger, I can make it run on iOS if I use different (physical) server that hosts the API's that I query with await client.PostAsync().
So,
https://somedomainname.com - works fine
https://someotherserver.com - does not work
To make it even more strange, both servers run exactly the same webservice, there is 0 difference, the only difference is the servers are on different sites with different client. So the fault is not with the queried webservice throwing timing out or cancelling the request.
Both servers have valid (trusted) SSL certificate so it's not an SSL related issue.
I am stuck. I have already tried ModernHttpClient and increaing the client.TimeSpan.
I also checked the ex.CancellationToken.IsCancellationRequested, which is false, so it's pretty safe to assume it was some time of "timeout". However, when I run the Post request from PostMan manually against that API, it's clear the webservice works fine (there is no timeout).
public class TokenService
{
private HttpClient client;
private App app;
public TokenService()
{
//client = new HttpClient();
client = new HttpClient(new NativeMessageHandler());
client.Timeout = TimeSpan.FromMinutes(30);
client.MaxResponseContentBufferSize = 256000;
app = (App)Application.Current;
}
public class TokenResponse
{
public String access_token;
public Int32 expires_in;
}
public async Task<String> GetNewToken()
{
// check if valid token already stored and within expiry DateTime
if(Token.BearerToken != null)
{
if (Token.expiry > DateTime.Now)
return Token.BearerToken;
}
var tokenUlr = app.WebServicesHostName + "/token";
var tokenResponse = new TokenResponse();
try
{
var content = new FormUrlEncodedContent(new[]
{
// this needs to be stored somewhere safe:
new KeyValuePair<string, string>("username", "validusernamehere"),
new KeyValuePair<string, string>("password", "validpasshere")
});
var response = await client.PostAsync(tokenUlr, content); // <<***EXCEPTION HERE
if (response.StatusCode == HttpStatusCode.OK)
{
var token = await response.Content.ReadAsStringAsync();
tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(token);
Token.BearerToken = tokenResponse.access_token;
Token.expiry = DateTime.Now.AddSeconds(tokenResponse.expires_in - 30);
return tokenResponse.access_token;
}
else
{
return null;
}
}
catch(TaskCanceledException ex)
{
String message = ex.Message;
String source = ex.Source;
CancellationToken token = ex.CancellationToken;
return null;
}
catch(Exception ex)
{
String test = ex.Message;
return null;
}
}
}
Exception details:
{System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess
(System.Threading.Tasks.Task task) [0x0002d] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:179
at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification
(System.Threading.Tasks.Task task) [0x0002e] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:156
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd
(System.Threading.Tasks.Task task) [0x0000b] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:128
at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult ()
[0x00000] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:357
at System.Net.Http.HttpClientHandler+c__async0.MoveNext ()
[0x004c1] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/System.Net.Http/System.Net.Http/HttpClientHandler.cs:391
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw ()
[0x0000c] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:143
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess
(System.Threading.Tasks.Task task) [0x00027] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:176
at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification
(System.Threading.Tasks.Task task) [0x0002e] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:156
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd
(System.Threading.Tasks.Task task) [0x0000b] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:128
at
System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[TResult].GetResult
() [0x00000] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:535
at System.Net.Http.HttpClient+c__async0.MoveNext ()
[0x000a9] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/System.Net.Http/System.Net.Http/HttpClient.cs:276
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw ()
[0x0000c] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:143
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess
(System.Threading.Tasks.Task task) [0x00027] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:176
at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification
(System.Threading.Tasks.Task task) [0x0002e] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:156
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd
(System.Threading.Tasks.Task task) [0x0000b] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:128
at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult ()
[0x00000] in
/Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.128/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:357
at XamarinMO.Services.TokenService+d__4.MoveNext ()
[0x000e3] in C:\Users\johns\OneDrive\Sources\WinMan API and Xamain
App\XamarinMO\XamarinMO\XamarinMO\Services\TokenService.cs:67 }

After many MANY hours, I found the solution:
It can be fixed by changing the TLS setting under project properties to Mono instead of Apple. This is due to a bug in the new Apple TLS default setting for SSL/TLS on iOS.

Related

Xamarin.forms ios MonoTouchException on Navigation.RemovePage

In my xamarin.forms ios application I have 4 ContentPages and 1 Popup(Rg.Plugin)
My Navigation is like this
Page 1--> Page 2 --> Page 3 --> Page 4 --> Popup
I want to navigate to Page 1 from a button click in Popup by removing the pages(2,3,4 and Popup).
I done it like this.
private void OK_Clicked(object sender, EventArgs e)
{
try
{
var countPagesToRemove = 3;
var mainPage = (Application.Current.MainPage as NavigationPage);
for (var i = 1; i < countPagesToRemove; i++)
{
mainPage.Navigation.RemovePage(mainPage.Navigation.NavigationStack[mainPage.Navigation.NavigationStack.Count - 2]);
}
Navigation.PopAsync();
Task.Delay(5);
PopupNavigation.Instance.PopAsync();
}
catch (Exception ex)
{
DisplayAlert("Result", ex.Message, "ok");
}
}
This works perfectly on Android.In ios it throws me this exception.
Foundation.MonoTouchException
Message=Objective-C exception thrown. Name: CALayerInvalidGeometry Reason: CALayer bounds contains NaN: [0 0; 0 nan]
Native stack trace:
Source=Xamarin.iOS
StackTrace:
at ObjCRuntime.Runtime.ThrowNSException (System.IntPtr ns_exception) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.14.1.39/src/Xamarin.iOS/ObjCRuntime/Runtime.cs:406
at ObjCRuntime.Runtime.throw_ns_exception (System.IntPtr exc) [0x00000] in /Users/builder/jenkins/workspace/xamarin-macios/xamarin-macios/runtime/Delegates.generated.cs:128
at (wrapper native-to-managed) ObjCRuntime.Runtime.throw_ns_exception(intptr)
at (wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain(int,string[],intptr,intptr)
at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.14.1.39/src/Xamarin.iOS/UIKit/UIApplication.cs:86
at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0000e] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.14.1.39/src/Xamarin.iOS/UIKit/UIApplication.cs:65
I have no idea what does this mean. I have searched and many people got this issues in several situations.But I didn't something like my scenerio.How to solve this?Any help is appreciated.

System.ArgumentException: Couldn't bind to method 'GetGetDefaultCipherSuitesHandler'

I am testing an app and seeing crashes on certain Android devices. The error we are seeing in the device logs (and Crashlytics):
Fatal Exception: android.runtime.JavaProxyThrowable: System.ArgumentException: Couldn't bind to method 'GetGetDefaultCipherSuitesHandler'.
at System.Delegate.GetCandidateMethod (System.Type type, System.Type target, System.String method, System.Reflection.BindingFlags bflags, System.Boolean ignoreCase, System.Boolean throwOnBindFailure) [0x000f9] in <e5404a7cbaab472a85c87c8c593feada>:0
at System.Delegate.CreateDelegate (System.Type type, System.Type target, System.String method, System.Boolean ignoreCase, System.Boolean throwOnBindFailure) [0x00014] in <e5404a7cbaab472a85c87c8c593feada>:0
at System.Delegate.CreateDelegate (System.Type type, System.Type target, System.String method) [0x00000] in <e5404a7cbaab472a85c87c8c593feada>:0
at Android.Runtime.AndroidTypeManager.RegisterNativeMembers (Java.Interop.JniType jniType, System.Type type, System.String methods) [0x00123] in <b9d3dca3be2f48d1874313a7a497190a>:0
at Android.Runtime.JNIEnv.RegisterJniNatives (System.IntPtr typeName_ptr, System.Int32 typeName_len, System.IntPtr jniClass, System.IntPtr methods_ptr, System.Int32 methods_len) [0x00115] in <b9d3dca3be2f48d1874313a7a497190a>:0
at (wrapper managed-to-native) Java.Interop.NativeMethods.java_interop_jnienv_alloc_object(intptr,intptr&,intptr)
at Java.Interop.JniEnvironment+Object.AllocObject (Java.Interop.JniObjectReference type) [0x00027] in <55c8950cd1f2461e9c76bad39e8fc8a4>:0
at Java.Interop.JniType.AllocObject () [0x0000c] in <55c8950cd1f2461e9c76bad39e8fc8a4>:0
at Java.Interop.JniPeerMembers+JniInstanceMethods.StartCreateInstance (System.String constructorSignature, System.Type declaringType, Java.Interop.JniArgumentValue* parameters) [0x00044] in <55c8950cd1f2461e9c76bad39e8fc8a4>:0
at Javax.Net.Ssl.SSLSocketFactory..ctor () [0x00034] in <b9d3dca3be2f48d1874313a7a497190a>:0
at Xamarin.Android.Net.OldAndroidSSLSocketFactory..ctor () [0x00010] in <b9d3dca3be2f48d1874313a7a497190a>:0
at Xamarin.Android.Net.AndroidClientHandler.SetupSSL (Javax.Net.Ssl.HttpsURLConnection httpsConnection) [0x00027] in <b9d3dca3be2f48d1874313a7a497190a>:0
at Xamarin.Android.Net.AndroidClientHandler.SetupRequestInternal (System.Net.Http.HttpRequestMessage request, Java.Net.URLConnection conn) [0x0007b] in <b9d3dca3be2f48d1874313a7a497190a>:0
at Xamarin.Android.Net.AndroidClientHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x00236] in <b9d3dca3be2f48d1874313a7a497190a>:0
at System.Net.Http.HttpClient.SendAsyncWorker (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) [0x000ca] in <0029d260a26b48288c90ea4fe946b24c>:0
at MyObfuscatedAppName.Services.CharacterEmbeddedResourceDriver.<LoadCharacters>b__1_0 (System.IObserver`1[T] observer) [0x000ca] in <446b676d44a2438aa46540b6f67c24f8>:0
at System.Reactive.PlatformServices.ExceptionServicesImpl.Rethrow (System.Exception exception) [0x00006] in <9ce90c81f389405fa2d38b0e75e8871b>:0
at System.Reactive.ExceptionHelpers.Throw (System.Exception exception) [0x0000a] in <99f8205c51c44bb480747b577b8001ff>:0
at System.Reactive.Stubs+<>c.<.cctor>b__2_1 (System.Exception ex) [0x00000] in <99f8205c51c44bb480747b577b8001ff>:0
at System.Reactive.AnonymousSafeObserver`1[T].OnError (System.Exception error) [0x0000e] in <99f8205c51c44bb480747b577b8001ff>:0
at System.Reactive.Concurrency.ObserveOn`1+ObserveOnSink[TSource].OnErrorPosted (System.Object error) [0x00000] in <99f8205c51c44bb480747b577b8001ff>:0
at Android.App.SyncContext+<>c__DisplayClass2_0.<Post>b__0 () [0x00000] in <b9d3dca3be2f48d1874313a7a497190a>:0
at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in <b9d3dca3be2f48d1874313a7a497190a>:0
at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00009] in <b9d3dca3be2f48d1874313a7a497190a>:0
at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.31(intptr,intptr)
at mono.java.lang.RunnableImplementor.n_run(RunnableImplementor.java)
at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:30)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5479)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(NativeStart.java)
It would seem that the part of this stack trace that relates to our code:
at System.Net.Http.HttpClient.SendAsyncWorker (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) [0x000ca] in <0029d260a26b48288c90ea4fe946b24c>:0
at MyObfuscatedAppName.Services.CharacterEmbeddedResourceDriver.<LoadCharacters>b__1_0 (System.IObserver`1[T] observer) [0x000ca] in <446b676d44a2438aa46540b6f67c24f8>:0
Is probably happening when our LoadCharacters() method begins:
public IObservable<CharacterJsonRoot> LoadCharacters()
{
Debug.WriteLine("MyApp :: Load Characters");
return Observable.Create<CharacterJsonRoot>(async observer =>
{
if (CrossConnectivity.Current.IsConnected)
{
var client = new HttpClient();
client.BaseAddress = new Uri("https://myapp.obfuscated.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("/my/path/to/file/data.json");
if (response.IsSuccessStatusCode)
{
Debug.WriteLine("Loaded from server");
var data = await response.Content.ReadAsStringAsync();
var characters = DeserializeJson(data);
observer.OnNext(characters);
observer.OnCompleted();
}
else
{
Debug.WriteLine("Loaded from fallback");
var assembly = typeof(CharacterEmbeddedResourceDriver).GetTypeInfo().Assembly;
var stream = assembly.GetManifestResourceStream("MyApp.Services.Data.data.json");
using (var reader = new StreamReader(stream))
{
string result = await reader.ReadToEndAsync();
var characters = DeserializeJson(result);
observer.OnNext(characters);
observer.OnCompleted();
}
}
}
else
{
// write your code if there is no Internet available
Debug.WriteLine("Loaded from fallback & NO Internet available ");
var assembly = typeof(CharacterEmbeddedResourceDriver).GetTypeInfo().Assembly;
var stream = assembly.GetManifestResourceStream("MyApp.Services.Data.data.json");
using (var reader = new StreamReader(stream))
{
string result = await reader.ReadToEndAsync();
var characters = DeserializeJson(result);
observer.OnNext(characters);
observer.OnCompleted();
}
}
}).SubscribeOn(System.Reactive.Concurrency.TaskPoolScheduler.Default).ObserveOn(SynchronizationContext.Current);
}
Other than that, I really have no idea why this error is happening only on certain devices. We are testing using App Live in Browser Stack and it always crashes for Galaxy Note 3 and Samsung Tab 4. Yet it will never crash on the Samsung Galaxy S7, for example.
UPDATE:
It's only happening on Android 4.4 and lower. So I've updated my Android project by setting the minimum Android version required to be 5. However, I would still like to know why this error occurs and what it means, so I'm leaving this open.
I came across this issue too. Turns out it was overzealous Linking. Adding Mono.Android to Skip linking assemblies fixed it for me.

Xam.Plugin.Geolocator iOS app crashes if user rejects location permissions

I am currently on version 4.2.0 of the Geolocator Plugin.
Upon installing the app, the following prompt appears:
However, if the user selects Don't Allow, the app crashes with the following error:
Plugin.Geolocator.Abstractions.GeolocationException: A geolocation error occured: Unauthorized
at Plugin.Geolocator.GeolocatorImplementation+<StartListeningAsync>d__33.MoveNext () [0x000eb] in <51c894f634a24ed2b17b19807ba0f99e>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <cf9013b38a4e4129bd64785080dd2844>:0
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <cf9013b38a4e4129bd64785080dd2844>:0
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <cf9013b38a4e4129bd64785080dd2844>:0
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <cf9013b38a4e4129bd64785080dd2844>:0
at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <cf9013b38a4e4129bd64785080dd2844>:0
at Divco.App+<StartListening>d__11.MoveNext () [0x000b5] in <a1469ede5dd148df922c3455ac848705>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <cf9013b38a4e4129bd64785080dd2844>:0
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) [0x00000] in <cf9013b38a4e4129bd64785080dd2844>:0
at UIKit.UIKitSynchronizationContext+<Post>c__AnonStorey0.<>m__0 () [0x00000] in <b7935acd70e343049845d6fd73e5ec44>:0
at Foundation.NSAsyncActionDispatcher.Apply () [0x00000] in <b7935acd70e343049845d6fd73e5ec44>:0
--- End of stack trace from previous location where exception was thrown ---
It continues to throw this error when you reopen the app.
I have included my code for App.xaml.cs to see if I am missing a piece. I followed the tutorial on the Geolocator plugin site linked previously.
App.xaml.cs
async void StartListening()
{
if (CrossGeolocator.Current.IsListening)
return;
CrossGeolocator.Current.DesiredAccuracy = 10;
await CrossGeolocator.Current.StartListeningAsync(TimeSpan.FromSeconds(Settings.PingDurationSeconds), 1, true, new Plugin.Geolocator.Abstractions.ListenerSettings
{
AllowBackgroundUpdates = true,
PauseLocationUpdatesAutomatically = false
});
CrossGeolocator.Current.PositionChanged += PositionChanged;
CrossGeolocator.Current.PositionError += PositionError;
}
async void PositionChanged(object sender, PositionEventArgs e)
{
//If updating the UI, ensure you invoke on main thread
var position = e.Position;
if (!Settings.IsLoggedIn)
return;
else if (Settings.IsAuthExpired)
await loginStore.Refresh();
var output = "Full: Lat: " + $"{position.Latitude}" + " Long: " + $"{position.Longitude}";
output += "\n" + $"Time: {position.Timestamp}";
output += "\n" + $"Heading: {position.Heading}";
output += "\n" + $"Speed: {position.Speed}";
output += "\n" + $"Accuracy: {position.Accuracy}";
output += "\n" + $"Altitude: {position.Altitude}";
output += "\n" + $"Altitude Accuracy: {position.AltitudeAccuracy}";
Settings.CurrentPosition = position;
await dataStore.UpdateLocation(position.Latitude, position.Longitude);
Debug.WriteLine(output);
}
private void PositionError(object sender, PositionErrorEventArgs e)
{
Debug.WriteLine(e.Error);
//Handle event here for errors
}
async void StopListening()
{
if (!CrossGeolocator.Current.IsListening)
return;
await CrossGeolocator.Current.StopListeningAsync();
CrossGeolocator.Current.PositionChanged -= PositionChanged;
CrossGeolocator.Current.PositionError -= PositionError;
}
Ideally, I would like to alert the user that they must enable location tracking to use our app (as it is a major part of our service).

Unicode receiving with ASP.NET core websocket

I'm trying to build a websocket server using ASP.NET core 1.1 websocket middleware that can handle text messages. My strategy is to use a fixed-size buffer to keep reading and decoding until the websocket message ends.
The middleware setup goes like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
var logger = loggerFactory.CreateLogger("websocket");
app.UseWebSockets();
app.Use(async (http, next) =>
{
if (!http.WebSockets.IsWebSocketRequest)
{
await next();
return;
}
var websocket = await http.WebSockets.AcceptWebSocketAsync();
while (websocket.State == WebSocketState.Open)
{
var buffer = new ArraySegment<byte>(new byte[32]);
var charbuffer = new char[32];
try
{
var sb = new StringBuilder();
var decoder = Encoding.UTF8.GetDecoder();
//aha, we got a message
var detectResult = await websocket.ReceiveAsync(buffer, CancellationToken.None);
var receiveResult = detectResult;
while (!receiveResult.EndOfMessage)
{
var charLen = decoder.GetChars(buffer.Array, 0, receiveResult.Count, charbuffer, 0);
logger.LogInformation($"Decoded {charLen} byte(s) from wire");
sb.Append(charbuffer, 0, charLen);
receiveResult = await websocket.ReceiveAsync(buffer, CancellationToken.None);
}
var charLenFinal = decoder.GetChars(buffer.Array, 0, receiveResult.Count, charbuffer, 0);
logger.LogInformation($"Decoded {charLenFinal} byte(s) from wire");
sb.Append(charbuffer, 0, charLenFinal);
var message = sb.ToString();
logger.LogInformation($"decoded message: {message}");
await websocket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes("got it")), WebSocketMessageType.Text, true, CancellationToken.None);
}
catch (Exception ex)
{
logger.LogError(ex.Message);
logger.LogError(ex.InnerException?.Message ?? string.Empty);
}
}
});
}
Now the code works well for text that include only ASCII characters. But when I tried to send Unicode text message (Vietnamese) whose length is longer than the buffer size, an exception occurs
fail: websocket[0]
The remote party closed the WebSocket connection without completing the close handshake.
fail: websocket[0]
at System.Net.WebSockets.ManagedWebSocket.<ReceiveAsyncPrivate>d__60.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at MvcApp.Startup.<>c__DisplayClass5_0.<<Configure>b__0>d.MoveNext()
The exception occurs at the line
var detectResult = await websocket.ReceiveAsync(buffer, CancellationToken.None);
What could be the reason for this error?
I think this:
var charLenFinal = decoder.GetChars(buffer.Array, 0, receiveResult.Count, charbuffer, 0);
should be:
var charLenFinal = decoder.GetChars(buffer.Array, 0, receiveResult.Count, charbuffer, 0, true);
since:
https://msdn.microsoft.com/en-us/library/125z2etb(v=vs.110).aspx
Remember that the Decoder object saves state between calls to
GetChars. When the application is done with a stream of data, it
should set the flush parameter to true to make sure that the state
information is flushed. With this setting, the decoder ignores invalid
bytes at the end of the data block and clears the internal buffer
.

InvalidCastException in Xamarin iOS

I'm receiving an InvalidCastException when running my code in Xamarin iOS. This doesn't happen for .NET or Android - only for iOS. Here's the exception details:
at
Microsoft.Scripting.Interpreter.EqualInstruction+EqualInt64.DoCalculate
(System.Object l, System.Object r) [0x00000] in
/Users/builder/data/lanes/3426/6c3fee4d/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/Instructions/EqualInstruction.cs:84
at Microsoft.Scripting.Interpreter.ComparisonInstruction.Calculate
(System.Object l, System.Object r) [0x00015] in
/Users/builder/data/lanes/3426/6c3fee4d/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/Instructions/ComparisonInstruction.cs:44
at Microsoft.Scripting.Interpreter.ArithmeticInstruction.Run
(Microsoft.Scripting.Interpreter.InterpretedFrame frame) [0x00020] in
/Users/builder/data/lanes/3426/6c3fee4d/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/Instructions/ArithmeticInstruction.cs:45
at Microsoft.Scripting.Interpreter.Interpreter.Run
(Microsoft.Scripting.Interpreter.InterpretedFrame frame) [0x0001b] in
/Users/builder/data/lanes/3426/6c3fee4d/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/Interpreter.cs:126
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw ()
[0x0000c] in
/Users/builder/data/lanes/3426/6c3fee4d/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:143
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess
(System.Threading.Tasks.Task task) [0x00047] in
/Users/builder/data/lanes/3426/6c3fee4d/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:187
at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification
(System.Threading.Tasks.Task task) [0x0002e] in
/Users/builder/data/lanes/3426/6c3fee4d/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:156
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd
(System.Threading.Tasks.Task task) [0x0000b] in
/Users/builder/data/lanes/3426/6c3fee4d/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:128
at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult ()
[0x00000] in
/Users/builder/data/lanes/3426/6c3fee4d/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:357
at
XamarinFormsPortable.Models.TweetingViewModel+d__4.MoveNext
() [0x00328] in
C:\Projects\LinqToTwitter\Samples\Xamarin4\XamarinFormsPortable\XamarinFormsPortable\XamarinFormsPortable\Models\TweetingViewModel.cs:54
The code that causes the problem is a LINQ to Twitter query:
var ctx = new TwitterContext(auth);
ulong InitialTweetId = 1;
int MaximumTweetsToRetrieve = 20;
var tweetsQuery = from tweet in ctx.Status
where tweet.Type == StatusType.User &&
tweet.ScreenName == "reinoso_alvaro" &&
tweet.Text.Contains("#buenosdias") &&
tweet.IncludeRetweets == false &&
tweet.TrimUser == true &&
tweet.ExcludeReplies == true &&
tweet.SinceID == InitialTweetId &&
tweet.Count == MaximumTweetsToRetrieve
select tweet;
var tweetList = await tweetsQuery.ToListAsync();
The condition that causes the problem is SinceID, which is ulong. However, you can see from the stack trace that the last method is EqualInt64, which is long. I looked for every place where I worked with SinceID and they are all ulong. The exception occurs on the last line: awaiting tweetsQuery.ToListAsync()
The implementation of ToListAsync is:
public static async Task<List<T>> ToListAsync<T>(this IQueryable<T> query)
{
var provider = query.Provider as TwitterQueryProvider;
IEnumerable<T> results = (IEnumerable<T>)await provider.ExecuteAsync<IEnumerable<T>>(query.Expression).ConfigureAwait(false);
return results.ToList();
}
ExecuteAsync runs and returns results. Then the exception occurs when materializing via results.ToList().
This is only happening on iOS.
A possible workaround is to cast the Status.SinceID property in the expression:
[...]
&& (long)tweet.SinceID == sinceStatusId // sinceStatusId is a long
[...]
Interesting point, Mono's Interpreter object comparison instructions for unsigned integers are assigned instances of their signed counterparts (https://github.com/mono/mono/blob/mono-4.8.0-branch/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/Instructions/EqualInstruction.cs , line 155 to 157):
case TypeCode.UInt16: return _UInt16 ?? (_UInt16 = new EqualInt16());
case TypeCode.UInt32: return _UInt32 ?? (_UInt32 = new EqualInt32());
case TypeCode.UInt64: return _UInt64 ?? (_UInt64 = new EqualInt64());
Not sure if this is intentional or not.

Resources