-
Notifications
You must be signed in to change notification settings - Fork 10.2k
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
Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions without reflection ? #31561
Comments
This must be the tip of the iceberg. We can change small things if people send pull requests but without a way to stop future regressions with tests it'll keep breaking in the future |
Like David said, until we have AOT testing we wont be able to make any significant improvements for AOT environments. If you would like to send a PR we can accept that, otherwise we wont be fixing this issue specifically right now. Also, what was the actual error you were getting? A nullref? |
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process. |
Thanks for the feedback. I am investigating further on my side whether the requested change would solve my issue, but I have a new MacBook Air ARM and building aspnetcore on that one seems not easy/possible. Will have to switch to my MacBook Pro but that will be for next weekend.
… On 7 Apr 2021, at 22:12, msftbot[bot] ***@***.***> wrote:
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process <https://github.com/dotnet/aspnetcore/blob/master/docs/TriageProcess.md>.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGFWKLTICCWI5QZDA5DTHS4CDANCNFSM42O4F65Q>.
|
I don't think it will. |
Here some more info after doing a lot of testing this weekend; I have the feeling it is almost there, just still looking for how to include my custom Kestrel build in the NativeAOT flow.
First, let me give you some more background about the feature I am looking for: the idea is to make some simple web api's, developed in C#, making use of Kestrel, but natively compiled for size, speed and C++ alike code protection.
Based on some examples I found, I came to the following program:
```C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
namespace tinykestrel
{
class Program
{
static async Task Main(string[] args)
{
NullLoggerFactory nullLoggerFactory = new NullLoggerFactory();
SocketTransportOptions socketTransportOptions = new SocketTransportOptions();
SocketTransportFactory socketTransportFactory = new SocketTransportFactory(
new OptionsWrapper<SocketTransportOptions>(socketTransportOptions), nullLoggerFactory);
KestrelServerOptions kestrelServerOptions = new KestrelServerOptions();
/* This ApplicationServices configuration is required to make .NET5 and .NET6 work,
but it prohibits NativeAOT without reflection */
/*
kestrelServerOptions.ApplicationServices = new ServiceCollection()
.AddLogging()
.BuildServiceProvider();
*/
kestrelServerOptions.ListenLocalhost(5001, listenOptions =>
{
X509Certificate2 serverCertificate = new X509Certificate2("server.pfx", "xxx");
listenOptions.UseHttps(serverCertificate);
});
using (KestrelServer kestrelServer =
new KestrelServer(new OptionsWrapper<KestrelServerOptions>(kestrelServerOptions),
socketTransportFactory,
nullLoggerFactory
)
)
{
await kestrelServer.StartAsync(new HttpApplication(), CancellationToken.None);
Console.ReadLine();
}
}
}
public class HttpApplication : IHttpApplication<IFeatureCollection>
{
public IFeatureCollection CreateContext(IFeatureCollection contextFeatures)
{
return contextFeatures;
}
public void DisposeContext(IFeatureCollection context, Exception exception)
{
}
public async Task ProcessRequestAsync(IFeatureCollection features)
{
IHttpRequestFeature request = (IHttpRequestFeature)features[typeof(IHttpRequestFeature)];
IHttpResponseFeature response = (IHttpResponseFeature)features[typeof(IHttpResponseFeature)];
IHttpResponseBodyFeature responseBody = (IHttpResponseBodyFeature)features[typeof(IHttpResponseBodyFeature)];
//IHttpConnectionFeature connection = (IHttpConnectionFeature)features[typeof(IHttpConnectionFeature)];
string requestBodyString;
Dictionary<string, object> jsonObjects = null;
using (StreamReader streamReader = new StreamReader(request.Body))
{
requestBodyString = await streamReader.ReadToEndAsync();
if (requestBodyString != string.Empty)
{
jsonObjects = await JsonSerializer.DeserializeAsync<Dictionary<string, object>>(new MemoryStream(Encoding.UTF8.GetBytes(requestBodyString)));
jsonObjects.Add("extra 1", "hey, I just changed your json");
}
}
if (jsonObjects == null)
{
jsonObjects = new Dictionary<string, object>();
jsonObjects.Add("line 1", "please send me some json and I will send it back to you");
jsonObjects.Add("line 2", "you can use me if you like");
jsonObjects.Add("line 3", "do no use subobjects in the json, I am a simple web api");
}
string responseBodyString = JsonSerializer.Serialize(jsonObjects);
byte[] responseBodyBytes = Encoding.UTF8.GetBytes(responseBodyString);
response.Headers.ContentLength = responseBodyBytes.Length;
response.Headers.Add("Content-Type", "application/json");
await responseBody.Stream.WriteAsync(responseBodyBytes, 0, responseBodyBytes.Length);
}
}
}
```
Without the https configuration lines:
```C#
kestrelServerOptions.ListenLocalhost(5001, listenOptions =>
{
X509Certificate2 serverCertificate = new X509Certificate2("server.pfx", "xxx");
listenOptions.UseHttps(serverCertificate);
});
```
I can natively compile and run this on my Mac. However, when I add the UseHttps lines above without:
```C#
kestrelServerOptions.ApplicationServices = new ServiceCollection()
.AddLogging()
.BuildServiceProvider();
```
Then I indeed get as you replied a Null reference error on the lines mentioned in the Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.
I managed to compile the Kestrel project with the requested change implemented locally on my Mac and then in debug mode the above program runs fine again, also with the UseHttps and without the kestrelServerOptions.ApplicationServices configured.
The only thing I am missing at the moment is how to include my custom built Microsoft.AspNetCore.Server.Kestrel.Core.dll and Microsoft.AspNetCore.Server.Kestrel.dll in the NativeAOT compilation on Mac. I am pretty sure if I get there, the above will work and opens a nice solution to develop simple natively compiled web api's with Kestrel in C#. So it would be nice if you could include the 2 question marks to avoid the Null reference error.
|
Instead of doing this:
Implement a custom IServiceProvider that does this: private class ServiceProvider : IServiceProvider
{
public object GetService(Type serviceType)
{
if (serviceType == typeof(ILoggerFactory))
{
return NullLoggerFactory.Instance;
}
return null;
}
} |
Will this avoid then using the dependency injection ? Because that seems to be the problem to natively compile without reflection.
… On 11 Apr 2021, at 17:29, David Fowler ***@***.***> wrote:
Instead of doing this:
kestrelServerOptions.ApplicationServices = new ServiceCollection()
.AddLogging()
.BuildServiceProvider();
Implement a custom IServiceProvider that does this:
private class ServiceProvider : IServiceProvider
{
public object GetService(Type serviceType)
{
if (serviceType == typeof(ILoggerFactory))
{
return NullLoggerFactory.Instance;
}
return null;
}
}
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGESLP4MJTLRZ432EGTTIG56VANCNFSM42O4F65Q>.
|
Yes, you're basically implementing your own |
I will try later today. Thanks.
… On 11 Apr 2021, at 17:32, David Fowler ***@***.***> wrote:
Will this avoid then using the dependency injection ? Because that seems to be the problem to natively compile without reflection.
Yes, you're basically implementing your own IServiceProvider and it can do whatever you want. Like manually create objects.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGGOYAZLK6HCXQRO263TIG6I7ANCNFSM42O4F65Q>.
|
You can use the HttpContext directly in your application as well. You might want to use something like https://github.com/benaadams/Ben.Http which wraps these Kestrel APIs and lets you create the server directly. cc @benaadams you might need to test https with your server, |
Regarding this one: any idea where to put this in the program.cs ? Tried in the class Program, but doesn't help. Would wonder how it could make the link with the Kestrel server.
… On 11 Apr 2021, at 17:29, David Fowler ***@***.***> wrote:
Instead of doing this:
kestrelServerOptions.ApplicationServices = new ServiceCollection()
.AddLogging()
.BuildServiceProvider();
Implement a custom IServiceProvider that does this:
private class ServiceProvider : IServiceProvider
{
public object GetService(Type serviceType)
{
if (serviceType == typeof(ILoggerFactory))
{
return NullLoggerFactory.Instance;
}
return null;
}
}
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGESLP4MJTLRZ432EGTTIG56VANCNFSM42O4F65Q>.
|
This is a lot more complex, but can look into this. Hope it supports https, because the http is working perfectly, it is just the https which causes the issue.
… On 11 Apr 2021, at 17:37, David Fowler ***@***.***> wrote:
You can use the HttpContext directly in your application as well. You might want to use something like https://github.com/benaadams/Ben.Http <https://github.com/benaadams/Ben.Http> which wraps these Kestrel APIs and lets you create the server directly.
cc @benaadams <https://github.com/benaadams> you might need to test https with your server,
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGEOX6VJDR3A6SLILHTTIG63RANCNFSM42O4F65Q>.
|
Ok, got this one: kestrelServerOptions.ApplicationServices = new ServiceProvider();
Will try whether the NativeAOT likes this.
… On 11 Apr 2021, at 17:40, Luc Lefever-Teughels ***@***.***> wrote:
Regarding this one: any idea where to put this in the program.cs ? Tried in the class Program, but doesn't help. Would wonder how it could make the link with the Kestrel server.
> On 11 Apr 2021, at 17:29, David Fowler ***@***.*** ***@***.***>> wrote:
>
>
> Instead of doing this:
>
> kestrelServerOptions.ApplicationServices = new ServiceCollection()
> .AddLogging()
> .BuildServiceProvider();
> Implement a custom IServiceProvider that does this:
>
> private class ServiceProvider : IServiceProvider
> {
> public object GetService(Type serviceType)
> {
> if (serviceType == typeof(ILoggerFactory))
> {
> return NullLoggerFactory.Instance;
> }
>
> return null;
> }
> }
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGESLP4MJTLRZ432EGTTIG56VANCNFSM42O4F65Q>.
>
|
This solution reacts exactly the same with NativeAOT: it should be able to avoid the call in ListenOptionsHttpsExtensions after ApplicationServices
var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices?.GetRequiredService<ILoggerFactory>() ?? NullLoggerFactory.Instance;
… On 11 Apr 2021, at 17:44, Luc Lefever-Teughels ***@***.***> wrote:
Ok, got this one: kestrelServerOptions.ApplicationServices = new ServiceProvider();
Will try whether the NativeAOT likes this.
> On 11 Apr 2021, at 17:40, Luc Lefever-Teughels ***@***.*** ***@***.***>> wrote:
>
> Regarding this one: any idea where to put this in the program.cs ? Tried in the class Program, but doesn't help. Would wonder how it could make the link with the Kestrel server.
>
>> On 11 Apr 2021, at 17:29, David Fowler ***@***.*** ***@***.***>> wrote:
>>
>>
>> Instead of doing this:
>>
>> kestrelServerOptions.ApplicationServices = new ServiceCollection()
>> .AddLogging()
>> .BuildServiceProvider();
>> Implement a custom IServiceProvider that does this:
>>
>> private class ServiceProvider : IServiceProvider
>> {
>> public object GetService(Type serviceType)
>> {
>> if (serviceType == typeof(ILoggerFactory))
>> {
>> return NullLoggerFactory.Instance;
>> }
>>
>> return null;
>> }
>> }
>> —
>> You are receiving this because you authored the thread.
>> Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGESLP4MJTLRZ432EGTTIG56VANCNFSM42O4F65Q>.
>>
>
|
Just one extra remark: the Serialization I had put in as an example also seems to have problems with the AOT compilation; but that is probably not an issue for me as I will use a custom serializer. But to natively compile this and make it run, the lines:
jsonObjects = await JsonSerializer.DeserializeAsync<Dictionary<string, object>>(new MemoryStream(Encoding.UTF8.GetBytes(requestBodyString)));
and
string responseBodyString = JsonSerializer.Serialize(jsonObjects);
should be excluded and replaced by direct json strings.
|
I don't understand. There's no reflection here |
The line with the ? after ApplicationServices is my custom implementation locally:
var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices?.GetRequiredService<ILoggerFactory>() ?? NullLoggerFactory.Instance;
In the actual AspNetCore it is without ? after ApplicationServices:
var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService<ILoggerFactory>() ?? NullLoggerFactory.Instance;
As there is no ?, the ApplicationServices should be passed otherwise it generates the Null exception.
But if providing the ApplicationServices, the GetRequiredService is called and my feeling is that this one generates a typeof(ILoggerFactory) down the road, which is according to the NativeAOT documentation ok to compare 2 typeof's, but here I guess it is used to find it in a generic collection. Could this be the cause that the NativeAOT does not like this one ?
… On 11 Apr 2021, at 18:09, David Fowler ***@***.***> wrote:
This solution reacts exactly the same with NativeAOT: it should be able to avoid the call in ListenOptionsHttpsExtensions after ApplicationServices var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices?.GetRequiredService() ?? NullLoggerFactory.Instance;
I don't understand. There's no reflection here
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGHS2EKWOPSMXU7CZZLTIHCUHANCNFSM42O4F65Q>.
|
This is the stack trace without kestrelServerOptions.ApplicationServices -> ArgumentNull_Generic
***@***.*** test % /Users/LLT/Projects/tests/tinykestrel5/bin/release/net5.0/osx.11.0-x64/native/tinykestrel
Unhandled Exception: EETypeRva:0x005C3A90: ArgumentNull_Generic Arg_ParamName_Name, provider
StackTrace_AtWordMicrosoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider) + 0x6a
StackTrace_AtWordMicrosoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions, HttpsConnectionAdapterOptions) + 0x5a
StackTrace_AtWordtinykestrel.Program.<>c.<Main>b__0_0(ListenOptions) + 0x3e
StackTrace_AtWordMicrosoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.ListenLocalhost(Int32, Action`1) + 0x59
StackTrace_AtWordtinykestrel.Program.<Main>d__0.MoveNext() + 0xf1
StackTrace_EndStackTraceFromPreviousThrow
StackTrace_AtWordSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x1c
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xc5
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task) + 0x46
StackTrace_AtWordtinykestrel.Program.<Main>(String[]) + 0x20
StackTrace_AtWordtinykestrel!<BaseAddress>+0x32f8e5
This is the stack trace with kestrelServerOptions.ApplicationServices -> Reflection_Disabled
Unhandled Exception: EETypeRva:0x005C4DA8: Reflection_Disabled
StackTrace_AtWordInternal.Reflection.RuntimeTypeInfo.get_Assembly() + 0x84
StackTrace_AtWordSystem.Resources.ResourceManager..ctor(Type) + 0x45
StackTrace_AtWordMicrosoft.AspNetCore.Server.Kestrel.Core.CoreStrings.get_ResourceManager() + 0x3b
StackTrace_AtWordMicrosoft.AspNetCore.Server.Kestrel.Core.CoreStrings.get_NetworkInterfaceBindingFailed() + 0xd
StackTrace_AtWordMicrosoft.AspNetCore.Server.Kestrel.Core.LocalhostListenOptions.<BindAsync>d__2.MoveNext() + 0x40b
StackTrace_EndStackTraceFromPreviousThrow
StackTrace_AtWordSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x1c
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xc5
StackTrace_AtWordMicrosoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.EndpointsStrategy.<BindAsync>d__2.MoveNext() + 0x192
StackTrace_EndStackTraceFromPreviousThrow
StackTrace_AtWordSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x1c
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xc5
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task) + 0x46
StackTrace_AtWordMicrosoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.<BindAsync>d__0.MoveNext() + 0x196
StackTrace_EndStackTraceFromPreviousThrow
StackTrace_AtWordSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x1c
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xc5
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task) + 0x46
StackTrace_AtWordMicrosoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.<BindAsync>d__32.MoveNext() + 0x2ed
StackTrace_EndStackTraceFromPreviousThrow
StackTrace_AtWordSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x1c
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xc5
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task) + 0x46
StackTrace_AtWordMicrosoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.<StartAsync>d__29`1.MoveNext() + 0x310
StackTrace_EndStackTraceFromPreviousThrow
StackTrace_AtWordSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x1c
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xc5
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task) + 0x46
StackTrace_AtWordtinykestrel.Program.<Main>d__0.MoveNext() + 0x206
StackTrace_EndStackTraceFromPreviousThrow
StackTrace_AtWordSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x1c
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xc5
StackTrace_AtWordSystem.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task) + 0x46
StackTrace_AtWordtinykestrel.Program.<Main>(String[]) + 0x20
StackTrace_AtWordtinykestrel!<BaseAddress>+0x32f7f5
|
@LLT21 Put the project on github with instructions and I'll try to get it working with no reflection model. I'm not understanding why the code I gave you doesn't work. |
Do I put it in a separate github project and send you the link ? Appreciate your efforts.
… On 11 Apr 2021, at 20:25, David Fowler ***@***.***> wrote:
@LLT21 <https://github.com/LLT21> Put the project on github with instructions and I'll try to get it working with no reflection model. I'm not understanding why the code I gave you doesn't work.
cc @MichalStrehovsky <https://github.com/MichalStrehovsky>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGE677EBJA33DZHLP5TTIHSSBANCNFSM42O4F65Q>.
|
Yes and just paste it here, but also with instructions on how you got those errors. |
The project is on: https://github.com/LLT21/nativeaotkestrel <https://github.com/LLT21/nativeaotkestrel>.
If you run it in debug mode, it works as is; once running, connect to: https://localhost:5001/ <https://localhost:5001/>.
However, if you compile in release mode with the native AOT (using dotnet publish -r osx.11.0-x64 -c release) it generates in the native folder an executable, which does generate the error: Reflection_Disabled.
The current project references .NET 5, but it is identical with .NET 6.
… On 11 Apr 2021, at 20:30, David Fowler ***@***.***> wrote:
Yes and just paste it here, but also with instructions on how you got those errors.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGCPBZZDWIDFQQCLNR3TIHTFNANCNFSM42O4F65Q>.
|
I'm going to assume windows vs OSX doesn't matter but we'll see. |
Yep, I can reproduce the issue on windows as well. |
If you would put in the question mark in the listenoptionshttpsextensions and with this changed code generate with nativeaot, should be ok. I just could not make the link between the change and the native aot compiler.
… On 12 Apr 2021, at 00:34, David Fowler ***@***.***> wrote:
Yep, I can reproduce the issue on windows as well.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
I think this would be solvable - the framework also has resource strings all over the place, but it offers a mode where all of that can be stripped - I'm assuming resource strings in ASP.NET are used for the same reason as in the framework (exception messages) and they're not essential. The bigger pictureThe reflection free mode in NativeAOT is very aggressive - I wouldn't really expect it to be useful for much more than simple command line apps. It's more of a tech demo of how the NativeAOT runtime can operate without any of the traditional .NET metadata and still run C# code. With the trimming annotations we're adding in .NET 5/6, NativeAOT compiler is going to be in a better position to reason about what needs to be reflectable within the app. The compiler currently assumes that whatever was compiled, also needs to be reflectable - that's why there's a pretty big size/memory difference in a Hello World compiled with default settings vs in the reflection-free mode. We already run the equivalent of the IL Linker trimming analysis in the Native AOT compiler, but it currently only makes sure we include extra code/data on top of what was discovered statically. I'm hoping in the coming weeks I'll get a chance to sit down and tweak this so that we have a mode where only the results of the trimming analysis are reflectable. I think this mode will get us pretty close to the reflection-free numbers, but will be immensely more useful for workloads like ASP.NET. |
I would like to do some further testing with my custom build, but have problems with the nuget configuration. The steps I took on OSX:
4a. <FrameworkReference Include="Microsoft.AspNetCore.App" Version="6.0.0-dev" /> in project file + I replaced the It was not possible to find any compatible framework version
4b. Added the dll's generated in .../artifacts/installers/debug/packs/Microsoft.AspNetCore.App.Ref/6.0.0-dev/ref/net6.0 to a subfolder net60 of my project and then tried to link the dll's in the project file with
This gives a BadImageFormatException: "Cannot load a reference assembly for execution." 4c. Tried to add the compiled AspNetCore via nuget: LLT@lmp tinykestrel6 % dotnet nuget list source
In combination with for the involved dll's:
This gives me an error: I am missing some info here how exactly to include the compiled AspNetCore libraries in my Visual Studio Code project. Probably because working with nuget on local projects is the first time I try. |
I would just use the DLL and do |
David, Michal,
I went around the iceberg in the last 2 weeks. Here are the results of my testing.
There were 2 types of aspnetcore issues with the native compilation of my test project in https://github.com/LLT21/NativeAOT5:
1) I had to change the lines https://github.com/dotnet/aspnetcore/blob/af981028f48078eaa42b1cc66658424abcb296e1/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs#L74 and https://github.com/dotnet/aspnetcore/blob/af981028f48078eaa42b1cc66658424abcb296e1/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs#L130 to:
_logger = new NativeLogger<HttpsConnectionMiddleware>(loggerFactory, "Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware");
like in https://github.com/LLT21/NativeAOT5/blob/24cc92d1fb47489e05a2b0c19b0ebec40b088026/kestrel/HttpsConnectionMiddleware.cs#L123 ; the new logger class is in the same file https://github.com/LLT21/NativeAOT5/blob/24cc92d1fb47489e05a2b0c19b0ebec40b088026/kestrel/HttpsConnectionMiddleware.cs#L46.
2) As of line https://github.com/dotnet/aspnetcore/blob/af981028f48078eaa42b1cc66658424abcb296e1/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs#L513
I also had to replace the CoreStrings with a NativeCoreStrings alternative as in: https://github.com/LLT21/NativeAOT5/blob/24cc92d1fb47489e05a2b0c19b0ebec40b088026/kestrel/HttpsConnectionMiddleware.cs#L30 and https://github.com/LLT21/NativeAOT5/blob/24cc92d1fb47489e05a2b0c19b0ebec40b088026/kestrel/HttpsConnectionMiddleware.cs#L555
One more CoreStrings value I had to replace in https://github.com/dotnet/aspnetcore/blob/af981028f48078eaa42b1cc66658424abcb296e1/src/Servers/Kestrel/Core/src/Internal/Infrastructure/TransportConnectionManager.cs#L82 (similar issue). These CoreStrings are used even without generating an exception.
For the CoreStrings I tried with the parameterized compilation proposal of Michal, but that didn't work on my side. Maybe because sometimes there are values inserted in the strings ?
By making these 2 types of changes, I can now run Kestrel https natively compiled. Great !
Then I wanted to fetch some data from a SQL Server database, also native. Unfortunately the native compiled program gave up:
1) With System.Data.SqlClient at the Open of the SqlConnection
2) With Microsoft.Data.SqlClient at the creation of the SqlConnection
What worked for me was to switch to System.Data.Odbc. That natively runs and I can read records from SQL Server with the DataReader. At the moment, the SQL Server ODBC driver does not yet exist for my OSX ARM, but it does work on my Intel one. I have also seen on the internet that the ARM ODBC driver is planned for July.
Finally I wanted to convert the result from SQL Server to Json. For reading the following code works natively: https://github.com/LLT21/NativeAOT5/blob/24cc92d1fb47489e05a2b0c19b0ebec40b088026/Program.cs#L109 ; for writing: https://github.com/LLT21/NativeAOT5/blob/24cc92d1fb47489e05a2b0c19b0ebec40b088026/Program.cs#L150 ; I also included what did not work in comments: https://github.com/LLT21/NativeAOT5/blob/24cc92d1fb47489e05a2b0c19b0ebec40b088026/Program.cs#L90
So basically with 2 types of changes in aspnetcore, switching to ODBC and some low level Json handling, I can do what I was looking for.
If you could include the 2 types of changes (probably there are better implementations than mine) in aspnetcore in your future plans, that would really help me and I guess many others. I am belonging to the 20% of developers who are looking at native AOT technology to obfuscate the code (dotnet/runtime#41522). Even if there is a tendency to more open source, some developers still would like to make a living from writing specialized code and protecting this.
In that respect I have 2 more general questions:
1) It would be ideal if there would be a list of some "special use cases" - like the one I am trying to achieve (web api connecting to SQL Server and returning json) - for which Microsoft offers a native AOT solution. For my use case, besides the 2 types of changes in aspnetcore, a SqlConnection with special overload that also natively runs would certainly help. And with some simple changes, I believe the higher level Json functions are not far from native support. I fully understand it will not be for all functionality, so a subset on which we can build if we like, is probably the best case.
2) Being pleasantly surprised that it actually works, I would like to ask Michal even if you make more native compilation options in the future, that you also keep the one without reflection: this is the best guarantee to protect the code we would like to protect. In some cases this can be important.
Hope the above helps. It has deviated from the original request for the better. Finally, let me say "thank you" for what we already can achieve in this area today and for the help building my trust.
Luc
… On 24 Apr 2021, at 19:41, David Fowler ***@***.***> wrote:
I would just use the DLL and do <Reference Include="PathToAssembly" />
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGDNGMODICQML5H3SO3TKL7FRANCNFSM42O4F65Q>.
|
@LLT21 Excellent work!
@MichalStrehovsky are there any guides on how to add support for "System.Globalization.Invariant" mode? Are you saying we would need to have a switch in our code to check for the AppContext setting and skip resource manager when that was set? Seems like something we could just bake into Resgen. How does the runtime implement this? Are there any pointers? The logger changes look easy to fix here without NativeLogger, so that's not a big deal but I fear that pattern is used pervasively and I would prefer if it were possible to fix the Logger<T> implementation. Though I'm not sure if that's possible (any ideas here @MichalStrehovsky ?) without runtime or compiler changes. The I could see an intrinsic like nameof(T) that would allow resolving the full name of the T at AOT/JIT time. That said, I don't have a problem making this change in Kestrel with a comment specifying that there's a narrow "no reflection" scenario for AOT here that this enables to keep it from regressing. As for JSON, we have a new source generator that you'll want to try out here that will allow you to keep using the JSON serializer but doesn't depend on reflection based metadata. (cc @layomia) I don't have any comments about SQL or ODBC but I'll cc @roji as an FYI. Finally, we don't have any tests for this scenario so it'll likely regress in strange ways until we have some basic scenario checked in that we run (this is similar to how we do handling linking managed code today). In the end, this is a very cool experiment and one that I'm happy to make changes (within reason) to keep it working for your scenarios. It'll also teach us more about AOT 😄 PS: There's some code clean up you can do, I'll send a pull request. |
The libraries implementation of
The "normal" implementation keys off the
What this descriptor does is that when IL Linker runs and this feature was specified as "enabled", illink will replace the method body of
This way one gets consistent experience whether illink runs or not (the appcontext switch disables the resources support if we didn't run linker, e.g. for If ASP.NET adds 1 and 2 and uses the same names for the AppContext switch and illink feature name, the 3 should just fall into place nicely. |
Forgot to respond to the other parts. First of all, @LLT21 - this is great work - I'm pleasantly surprised this works with Kestrel and DB and JSON with so little changes! I have no plans to remove the reflection-free mode - at minimum it's great for experimenting with the compiler and I want to keep it. In chatting with David about this, I realized this is still keeping some unnecessary metadata around - to get rid of the last bits of metadata, add |
Thank you all for the feedback. I will make a small example for the SqlClient team but then I will leave this issue a couple of days to work on my normal job as SQL Server BI engineer.
… On 26 Apr 2021, at 06:41, David Fowler ***@***.***> wrote:
@LLT21 <https://github.com/LLT21> Excellent work!
For the CoreStrings I tried with the parameterized compilation proposal of Michal, but that didn't work on my side. Maybe because sometimes there are values inserted in the strings ?
@MichalStrehovsky <https://github.com/MichalStrehovsky> are there any guides on how to add support for "System.Globalization.Invariant" mode? Are you saying we would need to have a switch in our code to check for the AppContext setting and skip resource manager when that was set? Seems like something we could just bake into Resgen. How does the runtime implement this? Are there any pointers?
@LLT21 <https://github.com/LLT21>
The logger changes look easy to fix here without NativeLogger, so that's not a big deal but I fear that pattern is used pervasively and I would prefer if it were possible to fix the Logger<T> <https://github.com/dotnet/runtime/blob/e5dd7150e6ced783159a8ae3adb77b899b1204db/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerT.cs#L29> implementation. Though I'm not sure if that's possible (any ideas here @MichalStrehovsky <https://github.com/MichalStrehovsky> ?) without runtime or compiler changes. The I could see an intrinsic like nameof(T) that would allow resolving the full name of the T at AOT/JIT time.
That said, I don't have a problem making this change in Kestrel with a comment specifying that there's a narrow "no reflection" scenario for AOT here that this enables to keep it from regressing.
As for JSON, we have a new source generator that you'll want to try out here that will allow you to keep using the JSON serializer but doesn't depend on reflection based metadata. (cc @layomia <https://github.com/layomia>)
I don't have any comments about SQL or ODBC but I'll cc @roji <https://github.com/roji> as an FYI.
Finally, we don't have any tests for this scenario so it'll likely regress in strange ways until we have some basic scenario checked in that we run (this is similar to how we do handling linking managed code today).
In the end, this is a very cool experiment and one that I'm happy to make changes (within reason) to keep it working for your scenarios. It'll also teach us more about AOT 😄
PS: There's some code clean up you can do, I'll send a pull request.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#31561 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AKO6WGBUXH46SSC2KCKIVQLTKTVGPANCNFSM42O4F65Q>.
|
@LL21 it was not obvious from your example initially. But if you do not use HTTPS then seems to be everything would be working without any patches. So if SSL would be handled by IIS or Nginx then you can use just previews. I think this is exciting. Technically from your starting point, you can have small MVC framework utilizing source generators for routing and for model binding. |
@kant2002 Sorry for not replying earlier, but I have been extremely busy with implementing a whole TechEmpower benchmark framework using the NativeAOT in a reflection free mode, with ODBC as an interface to PostgreSQL. Now regarding the TechEmpower benchmark, as I am a first time contributer, I am looking for someone who could approve my pull request which you can find here: TechEmpower/FrameworkBenchmarks#6614. Don't know what the normal process is for this, so if anyone of you would have the necessary rights, I would appreciate the review. Last question @davidfowl : do you expect me to make the changes in a pull request for this open issue or is it already planned ? I am not fully comfortable with this, but I can try if you like. |
@LLT21 thanks for this super interesting work.
Am curious... Have you given Npgsql a try instead of ODBC? There's definitely some reflection and runtime codegen in current released versions which could be problematic, but I've worked on removing those for 6.0 (see npgsql/npgsql#3300); Npgsql 6.0.0-preview4 already includes these changes. |
@roji Thanks for the feedback. I was not aware of the work in progress on Npgsql regarding this topic. Otherwise it could have saved me some sleepless odbc nights. Now that the implementation is there, I would like to first let the odbc test run and once these results are available, then compare with Npgsql. If performance is similar or better with Npgsql, I would certiainly favor the latter as it is much easier to implement. However, at the moment I am a bit confused how to proceed with the TechEmpower Benchmarks. According to this link TechEmpower/FrameworkBenchmarks#6614, a maintainer has to approve the running of workflows for first-time contributors (which I am). I cannot find a list of maintainers or how to get in contact. Do you have any idea ? |
The TechEmpower people are maintainers. I don't have a separate channel to them - not sure if anyone on this issue has. They look at these things but sometimes it takes weeks to add a new thing (clicking through other PR history). Updates get merged faster. |
@MichalStrehovsky The flow has started, so I am happy it goes this fast. Thanks for your feedback. |
@MichalStrehovsky One more question Michal: could you have a quick look at the project settings in https://github.com/LLT21/FrameworkBenchmarks/blob/appmpowerv1/frameworks/CSharp/appmpower/src/appMpower.csproj whether these are all optimal for fastest execution ? |
@LLT21 great. If you still run into Npgsql issues, we can try to iterate quickly to get to a point where it works. |
Yup, these all look good! Looking forward to see the results! |
@LLT21 if and when you get around to looking at Npgsql, check out npgsql/npgsql#3813 (comment). We've also added some tasks to npgsql/npgsql#3300, and there's a pretty good idea of what needs to be done (thanks to @NinoFloris). |
@roji I added today a repository https://github.com/LLT21/appmpower-npgsql with therein a class RawDbNpgsql. In the HttpApplication class there are conditional compilation statements for Npgsql or Odbc. In the RawDbNpgsql class I have also foreseen some Console.WriteLine statements to see up to where the native compilation goes. At the moment it ends before the creation of the new NpgsqlConnection, but I noticed in your repository you still did some recent changes I have not integrated yet. Feel free to test with the https://github.com/LLT21/appmpower-npgsql repository if you want. |
Yeah, we're working on it :) I'll ping you once we have something which should work. |
We have no plans to make no reflection mode work. |
I have tried to compile my .NET 5 bare http server project with Native AOT without reflection. This is possible for a normal http connection, but not for a https connection because in Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions the line of code:
that appears twice in this class requires reflection. Could it be changed to:
so that it has the ? after ApplicationServices. In this way I believe the reflection can be avoided.
The text was updated successfully, but these errors were encountered: