-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Don't call into unsupported APIs on build targets which don't support quic #49261
Conversation
Tagging subscribers to this area: @dotnet/ncl |
Can we instead do this with a few strategically placed Http3.IsSupported calls, skipping the affected code paths and also enabling the linker to remove most of the relevant code on platforms where it's false? I'd like to avoid littering the code base with these ifdefs. |
That will not work with the unsupported analyzer warning but if we are fine to pragma that then it could work but won't be as effective size-wise. |
I'd be fine suppressing the analyzer for the whole assembly for the problematic build flavor until the analyzer can be taught to understand such guards. I thought there was an issue about this but I can't find it now. We're blessing this IsSupported pattern as a recommended way to enable portions of code to be enabled/disabled from project files and run-time switches, and for the linker to recognize... we should find a way for that to be factored into the platform analyzer as well, e.g. having the analyzer see that it's hard-coded to return a particular value, reading the project file to see if there's a corresponding switch, having some way to express to the analyzer a mapping between IsSupported and various platform values, etc.
I would hope it's very close. Otherwise, seems we have larger issues to address. |
… quic The code could be extracted to HttpConnectionPool.quick.cs but not sure it's worth the split as that would move the majority of the code. Fixes dotnet#49201 Fixes dotnet#49187
The issue you're referring to @stephentoub is #44922 (comment). @buyaa-n had some preliminary designs for that, but it still needs more design effort. This can be tracked as another use case for platform guard assertions. |
031a02a
to
462934b
Compare
Updated the PR. |
This is blocking @marek-safar's team -- it broke SocketsHttpHandler in Preview 2 on mobile platforms. They would like to fix it in Preview 3 - ideally by 3/19. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but please also wait for @scalablecory or @ManickaP reviews
How is splitting the code into partial classes and conditionally compile them different from the ifdefs? I thought that:
means that we can guard the HTTP/3 stuff in place without splitting the code. Or am I wrong? I'd really like to understand this change and what the general consensus here is, so please bear with me. |
|
||
return GetHttp3ConnectionAsync(request, authority, cancellationToken); | ||
} | ||
var result = GetHttp3ConnectionAsync(request, cancellationToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you help me understand why all of these changes are needed?
My suggestion was to have just an IsHttp3Enabled or Http3.IsSupported property (the latter of which could be configured via the same mechanisms we use for other IsSupported properties) that was special-cased by platform. If the linker could see it was false, for example, then here GetHttp3ConnectionAsync shouldn't need to have been moved and stubbed out; it would never be invoked on platforms where it wasn't supported, and the linker would be able to remove it entirely, since all calls to it are guarded by IsHttp3Enabled/Http3.IsSupported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The quic apis were called from more places than expected and the simplest way to automatically check that no PNSE APIs are never called is not to compile them (the analyzer does not work here). If it was only a few methods it'd be fine to put IsHttp3Enabled everywhere but how do you ensure the PNSE code is never called and no future PR break it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how do you ensure the PNSE code is never called and no future PR break it?
Isn't that what the platform compatibility analyzer is for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not work here see #49278 and its method body limited so that's a lot of additional annotations to bubble it up to right IsHttp3Enabled check.
I'm taking over of this PR. I'll try to make the change suggested by @stephentoub (i.e. with correctly placed cc: @karelz @marek-safar |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the the problem one layer down to System.Net.Quic, which causes much less disturbance. The System.Net.Http stays as-is. Note that all the access to H3 code has already been guarded behind _http3Enabled
, I just fixed that the initialization won't blow up even on Android etc.
src/libraries/System.Net.Http/tests/UnitTests/HttpClientTest.cs
Outdated
Show resolved
Hide resolved
@@ -6,121 +6,124 @@ | |||
|
|||
namespace System.Net.Quic | |||
{ | |||
public static class QuicImplementationProviders | |||
public partial class QuicClientConnectionOptions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regenerated ref sources. The => throw null;
were causing the PNSE generator not to generate the stub.
{ | ||
internal sealed class NoQuicImplementationProvider : QuicImplementationProvider | ||
{ | ||
public override bool IsSupported => false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The linker won't be able to see this logic so the change no longer fixes #46915 issue. It will also trigger a platform compatibility warning which is probably not desirable which cannot be fix without pragma.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The linker won't be able to see this logic so the change no longer fixes #46915 issue.
I'll try to fix that, but I need a way how to test it and confirm it work as it should. ATM, I don't even know how the desired result looks let alone be able to confirm it.
It will also trigger a platform compatibility warning
How can I test this?
It's not only about the attributes, they are made to be consistent with the code. See for instance this part of the commit: |
Right, that guard makes sense, i meant only the attribute would have no effect so better to be removed |
I want to confirm we don't need to express that the API is supported on most flavors of Linux, but not on Android. Given this example, I interpret the intent to be:
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
[UnsupportedOSPlatform("android")]
private async ValueTask<(HttpConnectionBase connection, bool isNewConnection)>
GetHttp3ConnectionAsync(HttpRequestMessage request, HttpAuthority authority, CancellationToken cancellationToken) { ... } Since this is treated by the analyzer as an inconsistent list, I'm not sure how else we would capture that Android is an unsupported subset of Linux here. Did I understand the intent correctly, @ManickaP? |
<!-- Adds UnsupportedOSPlatform and SupportedOSPlatform attributes to the assembly when: | ||
* At least one <UnsupportedOSPlatforms /> or <SupportedOSPlatforms /> has been specified |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: Once this PR goes through, I plan to send a follow-up PR that utilizes the <SupportedOSPlatforms>
property instead of <IsWindowsSpecific>
. jeffhandley@0f530be
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs
Show resolved
Hide resolved
@jeffhandley @buyaa-n [SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
private async ValueTask<(HttpConnectionBase connection, bool isNewConnection)>
GetHttp3ConnectionAsync(HttpRequestMessage request, HttpAuthority authority, CancellationToken cancellationToken) { ... } means that it's supported on Linux but not supported on Android? Therefore, something like the following code should fail when build for Android: if (OperatingSystem.IsAndroid())
{
// Touching method that's supposed to be supported only on Linux, Windows, MacOS within an Android code.
return GetHttp3ConnectionAsync(request, authority, cancellationToken);
} If so, then it doesn't work. I just tried that and compilation finishes without an error. However, if I change the attributes to: [SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
[UnsupportedOSPlatform("android")]
private async ValueTask<(HttpConnectionBase connection, bool isNewConnection)>
GetHttp3ConnectionAsync(HttpRequestMessage request, HttpAuthority authority, CancellationToken cancellationToken) { ... } I will get a compilation error on Android and only on Android for: // Don't even need the if, the build will fail for Android and pass for anything else.
// if (OperatingSystem.IsAndroid())
{
// Touching method that's supposed to be supported only on Linux, Windows, MacOS.
return GetHttp3ConnectionAsync(request, authority, cancellationToken);
} Which is exactly what I'm trying to achieve. Edit: It doesn't work properly, this should fail for tvOS, iOS etc. as well, but doesn't. Seems like the analyzer doesn't do what I'd expect. |
Yes, only windows, osx and linux. It's not supported on mobile platforms therefore the
Sorry, I'm not following this question. |
Yes, [SupportedOSPlatform("windows"), SupportedOSPlatform("linux"), SupportedOSPlatform("macos")] means that it's supported on Linux but not supported on Android and it means
No, guard methods are not used for evaluating call site, call site supported by Android is when it has [SupportedOSPlatform("android")]
public void GetConnection()
{
// Here you should see the warning
return GetHttp3ConnectionAsync(request, authority, cancellationToken);
} |
I am not sure how you are tested, if you just used a guard method for checking tvOS, iOS then it wouldn't work. Try them in cross-platform context at least, then using a guard method could make sense |
Forget about the [SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
private async ValueTask<(HttpConnectionBase connection, bool isNewConnection)>
GetHttp3ConnectionAsync(HttpRequestMessage request, HttpAuthority authority, CancellationToken cancellationToken) { ... }
public async ValueTask<(HttpConnectionBase connection, bool isNewConnection)>
GetConnection(...)
{
...
// This line should cause a warning on Android/tvOS/iOS etc. but doesn't
return GetHttp3ConnectionAsync(request, authority, cancellationToken);
} Note that |
I see your point, that is actually an expected scenario, for example for [assembly:SupportedOSPlatform("Android")]
...
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
private async ValueTask<(HttpConnectionBase connection, bool isNewConnection)>
GetHttp3ConnectionAsync(HttpRequestMessage request, HttpAuthority authority, CancellationToken cancellationToken) { ... }
public async ValueTask<(HttpConnectionBase connection, bool isNewConnection)>
GetConnection(...)
{
...
// there will be no warning as it all are supported on Android
return GetHttp3ConnectionAsync(request, authority, cancellationToken);
} It would only make sense when the API is referenced from outside |
So it basically renders |
I guess annotations are dedicated more for external users and the correct annotation for the longer run will be |
Maybe I'm missing something but why is there no warning? It seems the API that is being called is known to be OS specific and doesn't have support for Android. Or this is the case we explicitly made not-warn based on Marek's earlier feedback? |
@terrajobst because for Android targeted build MSBuild would add assembly level
It is not exactly the same issue but introduced with the same cause (SDK adding a supported attribute for the target platform) |
LGTM |
|
||
<ItemGroup> | ||
<TestConsoleAppSourceFiles Include="HttpClientTest.cs"> | ||
<SkipOnTestRuntimes>browser-wasm</SkipOnTestRuntimes> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't disabling this test on wasm basically invalidate it? This test was trying to ensure Quic gets trimmed on non-supported platforms.
The only non-supported platform this test runs on is wasm. So I don't see the point of these tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It crashes on wasm, I don't know how to fix that. But I'd love to still have this test if possible.
I also thought we plan to widen the trimming test for platforms like Android, iOS, for which we definitely should have this test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "headers" failure indicated that http-related JS APIs were being accessed inside of v8. Whether or not that indicates the trimming was successful is hard to say, but i would confidently assert that no code relying on the ability to issue http requests is going to pass inside the v8 shell, so for that test to pass it needs to have the HTTP bits entirely mocked or shimmed out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we plan to widen the trimming test for platforms like Android, iOS,
That's the eventual plan, yes. It currently isn't scheduled though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So should I remove it for now? If so, I'll do it in a follow up PR, no problem, just say so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine to leave it. Once the other platforms come on line this will add value.
Fixes #49201
Fixes #49187
Fixes #46915