_From @ThomasLebrun on Thursday, October 3, 2019 11:57:55 AM_
I'm trying to use SignalR on an Apple Watch project (using WatchOS) but the application is unable to run correctly as it seems to not be a supported plateform (because the SignalR client keep using HttpClientHandler even when I force it to use NSUrlSessionHandler).
I saw some posts saying that NSUrlSessionHandler should be specified but even if I try, it keeps using HttpClientHandler.
Can someone confirm if SignalR client is available (and working) on WatchOS ?
Be able to run the SignalR client, start a connection to the Hub and to receive message
At runtime, the application keeps crashing with a PlateformNotSupportedException. The message states the System.Net.HttpClientHandler is not supported, althought it's specified, in csproj of extension projet (and main project), that client handler is NSUrlSessionHandler.
Trying to force it in the WithUrl option parameters gives the same results.
Create a Watch OS application. Add SignalR Client 3.0 and try to start connection to any Hub.
Thanks !
_Copied from original issue: SignalR/SignalR#4414_
althought it's specified, in csproj of extension projet (and main project), that client handler is NSUrlSessionHandler.
Maybe I'm misreading this, but are you trying to set the handler in your csproj?
In order to use a different handler you need to change some options on the HubConnection. Specifically HttpMessageHandlerFactory, you can see our docs about config here: https://docs.microsoft.com/aspnet/core/signalr/configuration?view=aspnetcore-3.0&tabs=dotnet#configure-additional-options
Hi Brennan,
I tried this (changing the HttpMessageHandlerFactory) and it's not working, I'm still having the PlatformNotSupportedExeption (that's why I've try to explain with "Trying to force it in the WithUrl option parameters gives the same results." :) ):
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/myhub", options => {
options.HttpMessageHandlerFactory = (handler) => return new NSUrlSessionHandler();
})
.Build();
That's why I tried to change it too in the csproj
Thanks,
Could you share the exception with the stack trace?
Hi @BrennanConroy ,
Here is stack trace:
2019-10-03 18:38:55.831443+0200 HonoreServerWatchAppWatchOSExtension[41517:3434015] *** Terminating app due to uncaught exception 'System.PlatformNotSupportedException', reason: 'System.Net.Http.HttpClientHandler is not supported on the current platform.'
*** First throw call stack:
(
0 CoreFoundation 0x008d33d2 __exceptionPreprocess + 370
1 libobjc.A.dylib 0x06a70616 objc_exception_throw + 49
2 HonoreServerWatchAppWatchOSExtensio 0x003388d9 xamarin_process_managed_exception + 1097
3 HonoreServerWatchAppWatchOSExtensio 0x00065c02 _ZL30native_to_managed_trampoline_4P11objc_objectP13objc_selectorPP11_MonoMethodj + 354
4 HonoreServerWatchAppWatchOSExtensio 0x0006638e -[__MonoMac_NSAsyncSynchronizationContextDispatcher xamarinApplySelector] + 62
5 libobjc.A.dylib 0x06a83742 -[NSObject performSelector:withObject:] + 59
6 Foundation 0x04e050eb __NSThreadPerformPerform + 265
7 CoreFoundation 0x00836b8f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
8 CoreFoundation 0x00836aa2 __CFRunLoopDoSource0 + 98
9 CoreFoundation 0x008361e7 __CFRunLoopDoSources0 + 183
10 CoreFoundation 0x00830a4a __CFRunLoopRun + 1594
11 CoreFoundation 0x008300e4 CFRunLoopRunSpecific + 404
12 CoreFoundation 0x00831391 CFRunLoopRunInMode + 129
13 GraphicsServices 0x095562ca GSEventRunModal + 73
14 GraphicsServices 0x095561b6 GSEventRun + 80
15 UIKitCore 0x0cdf137a UIApplicationMain + 1789
16 libxpc.dylib 0x07345412 _xpc_objc_main.cold.3 + 208
17 libxpc.dylib 0x0733189b _xpc_objc_main + 235
18 libxpc.dylib 0x073343ab xpc_main + 133
19 Foundation 0x04f8aac9 service_connection_handler + 0
20 PlugInKit 0x13e7f40f __PLUGINKIT_HANDING_CONTROL_TO_MAIN_SERVICE_LISTENER__ + 29
21 PlugInKit 0x13e871d1 __PLUGINKIT_CALLING_OUT_TO_CLIENT_SUBSYSTEM_FOR_BEGINUSING__ + 31920
22 WatchKit 0x0113cdf9 WKExtensionMain + 73
23 WatchKit 0x0113ce15 main + 11
24 HonoreServerWatchAppWatchOSExtensio 0x0008770c main + 44
25 HonoreServerWatchAppWatchOSExtensio 0x0034aa8b xamarin_main + 3387
26 HonoreServerWatchAppWatchOSExtensio 0x00087675 xamarin_watchextension_main + 85
27 libdyld.dylib 0x07018031 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
And, for reference, here is the code:
var hubConnection = new HubConnectionBuilder()
.WithUrl("https://myapi/event", o =>
{
o.HttpMessageHandlerFactory = (handler) =>
{
return new NSUrlSessionHandler();
};
})
//.WithAutomaticReconnect()
.Build();
if(hubConnection != null)
{
await hubConnection.StartAsync();
}
Thanks,
Interesting, so we can't even construct a HttpClientHandler on some platforms https://github.com/mono/mono/blob/master/mcs/class/System.Net.Http/System.Net.Http/HttpClientHandler.platformnotsupported.cs
@ triage:
Should we consider detecting PlatformNotSupported and falling back to only using the HttpMessageHandlerFactory in those situations? Users would need to do all their own configuration in that case which is fine.
It looks like you are hitting HttpClientHandler handler somewhere. As you rightly noticed the HttpClientHandler is not supported at all on WatchOS, you have to use NSUrlSessionHandler only to get things going.
/cc @mandel-macaque
Hi Marek,
Yes, I agree with you but as you can see in the code sample, I'm not using HttpClientHandler so can you confirm that the problem is inside the implementation of the SignalR client on WatchOS ?
Thanks,
@marek-safar the problem is that the framework is creating the handler, not the customer. We'd like to avoid having to make a build specifically for WatchOS. Why was that decision made to throw PNSE in HttpClientHandler on the WatchOS?
Here's the offending code in SignalR - https://github.com/aspnet/AspNetCore/blob/fd060ce8c36ffe195b9e9a69a1bbd8fb53cc6d7c/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs#L511.
The decision was to catch the developer's mistakes early. We can enable the HttpClientHandler ctor but I don't think that will solve your issue for two reasons.
static HttpMessageHandler CreateDefaultHandler () but that's private implementation.I'm still don't understand why the code can be written then:
o.HttpMessageHandlerFactory = (handler) =>
{
return new NSUrlSessionHandler();
};
If SignalR code is still setting some of HttpClientHandler properties which will throw PNSE, this should not be allowed (at least on plateform not supported), no ?
- You still need to pass platform-specific HttpClientHandler to HttpClient which right now there is no public API available to get it.
Why? It seems to me we shouldn't be assuming that people build specifically for WatchOS; we should consider multi-targeting as a last-resort. Ideally, basic scenarios should just work without having to instantiate platform provided types. Throwing for specific operations seems OK, but we should also work towards exposing capability APIs so that library authors can handle this generically too (and don't have to resort to OS checks or have to catch exceptions).
Breaking a type like HttpClientHandler's constructor or properties seems rough here. The API Catalog (cert's still broken @terrajobst 馃槤) doesn't give any indication that this API won't work. It seems very strange that I don't get a platform-specific implementation in HttpClientHandler. If there are individual properties that can't be supported in the platform-specific implementation, then we can look at guarding around that, but this doesn't seem like a very sustainable pattern if we're going to have to keep adding guards like this.
We can (and may) take a tactical fix to yield full control of the handler set-up to the app, but it feels unsustainable.
Also, is this the case for other APIs? There are a bunch of platform APIs we invoke that don't have extension points like this. For example, ClientWebSocket (though we are investigating adding one).
I think this needs to be fixed in mono鈥檚 watch OS implementation.
basic scenarios should just work without having to instantiate platform provided types
I believe we have that, if you create HttpClient it will use by default the platform specific handler by feeding HttpClient with custom HttpClientHandler you are skipping that which I think is expected behaviour.
HttpClientHandler due to historical reasons is generic handler based on .NET Sockets. On platforms where sockets are exposed, we support it with the exactly same name and public surface but if the platform has also its own Http API we try to map it to this .NET type and obviously we have to use a different name. I think this is more natural than the other way where we would name the most compatible .NET version of HttpClientHandler differently.
On WatchOS this is different as we have only one choice now but that does not mean in the future we won't be able to have fully compatible HttpClientHandler if Apple adds sockets support.
Think about what that means for library authors though. The options are:
NSUrlSessionHandler and set the applicable options on that.Both options are bad. HttpClientHandler should map the whatever the default handler is on the underlying platform, not necessarily a sockets based implementation. This seems to be the case on all platforms except for WatchOS (BTW we expect this to work with web assembly as well).
On WatchOS this is different as we have only one choice now but that does not mean in the future we won't be able to have fully compatible HttpClientHandler if Apple adds sockets support.
I haven't heard yet why this distinction was made? Why can't we map HttpClientHandler to NSUrlSessionHandler?
I agree the current situation is not optimal but I am trying to explain that it's not only about Watch. Any Xamarin platform which has more than 1 handlers won't be handled well by your existing code.
We can change WatchOS handler to be NSUrlSessionHandler based. However, the existing code still won't work with platform-specific handlers set for iOS or Android through the project setting which I think should be addressed too.
I believe we have that, if you create HttpClient it will use by default the platform specific handler by feeding HttpClient with custom HttpClientHandler you are skipping that which I think is expected behaviour.
This isn't true from an API perspective though. In all the other .NET platforms we work with, HttpClientHandler is a suitable default handler for the platform (regardless of implementation). For example, in .NET Core versions prior to 3.0 it was a WinHttp-based handler when running on Windows and a libcurl-based one on Linux. Those were implemented in separate implementation handlers but the HttpClientHandler was still a usable API. In .NET Core 3.0 it was converted to use Sockets.
There's nothing that indicates that HttpClientHandler any more a platform-specific API than HttpClient.
Given the current situation, we will likely do some kind of tactical fix around HttpClientHandler here because we are forced to in order to work around the issue. My main concern is moving forward, which APIs are we going to have to expect to work around this kind of issue? Is there any place that documents APIs in .NET Standard that do not work in certain Xamarin environments?
We'll investigate the tactical fix in 5.0. We're still quite concerned that we're going to have to cover other unknown gaps in the watchOS .NET Standard implementation.
The tactical fix still won't enable this scenario without special user configuration, which is concerning as well.
There's nothing that indicates that HttpClientHandler any more a platform-specific API than HttpClient.
What kind of indication would prefer? In general .NET Standard is API standard not implementation standard so there is no guarantee your code will run everywhere without testing.
We're going to have to cover other unknown gaps in the watchOS .NET Standard implementation
Correct and same applied to all other platforms.
We'll fix the default watchOS handler to allow sending the data but all properties and other settings will still throw PNSE because there is no support for them on the platform and we cannot emulate them either.
What kind of indication would prefer? In general .NET Standard is API standard not implementation standard so there is no guarantee your code will run everywhere without testing.
That's true, but developer expectations apply. HttpClientHandler is the more advanced way to tweak it and allow wrapping/mocking. It's very reasonable for library code to accept an HttpClientHandler rather than accepting an HttpClient directly. As @anurse said, developers would generally expect that instantiating HttpClientHandler will give them an instance of the platform provided handler.
As @anurse said, developers would generally expect that instantiating HttpClientHandler will give them an instance of the platform provided handler.
I think this needs more thinking. Our expectation was that the developers would prefer handlers which is most compatible across the platform. This is also the direction CoreFX went, e.g. Linux handler switched from libcurl which one can consider platform-specific handler to more general, more compatible fully managed socket handler. It's even worse for macOS? Does .NET Core default to native platform support for http or not. I don't think so.
Going forward I'd much rather see this exposed at API level instead of trying to be "smart" and make it hard for advanced libraries like this one to find the right setup.
Going forward I'd much rather see this exposed at API level instead of trying to be "smart" and make it hard for advanced libraries like this one to find the right setup.
That solution is bad for library authors. We have to optimize for not requiring specific builds for each platform here.
Corefx never broke HttpClientHandler when adding other handlers AFAIK
That solution is bad for library authors.
That's exactly the concern I have. If we expect libraries to pivot on the underlying platform, then our API portability story is back where it started (PCLs and such).
@marek-safar Did we land somewhere with how Xamarin will handle HttpClientHandler on other platforms (like watchOS)? I don't see us having the resources to do special work for watchOS in 5.0, so I'd like to understand what position we'll be in if we don't make a change here.
The plan for .NET5 is to try to abstract this a bit when you construct HttpClientHandler you'd get automatically the best handler under the hood. However, you still will have to be careful which properties you call on the handler as they might not all be supported everywhere.
However, you still will have to be careful which properties you call on the handler as they might not all be supported everywhere.
Yep, that makes total sense, and we've done that before.
Closing, .NET 5 should fix this.
Most helpful comment
Why? It seems to me we shouldn't be assuming that people build specifically for WatchOS; we should consider multi-targeting as a last-resort. Ideally, basic scenarios should just work without having to instantiate platform provided types. Throwing for specific operations seems OK, but we should also work towards exposing capability APIs so that library authors can handle this generically too (and don't have to resort to OS checks or have to catch exceptions).