Skip to content

Commit 1625f89

Browse files
authored
Trim regex from Grpc.AspNetCore (#2326)
1 parent 2bbb977 commit 1625f89

File tree

6 files changed

+85
-129
lines changed

6 files changed

+85
-129
lines changed

src/Grpc.AspNetCore.Server/GrpcServiceExtensions.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@ public static IGrpcServerBuilder AddGrpc(this IServiceCollection services)
5858
{
5959
ArgumentNullThrowHelper.ThrowIfNull(services);
6060

61-
services.AddRouting(options =>
62-
{
63-
// Unimplemented constraint is added to the route as an inline constraint to avoid RoutePatternFactory.Parse overload that includes parameter policies. That overload infers strings as regex constraints, which brings in
64-
// the regex engine when publishing trimmed or AOT apps. This change reduces Native AOT gRPC server app size by about 1 MB.
65-
AddParameterPolicy<GrpcUnimplementedConstraint>(options, GrpcServerConstants.GrpcUnimplementedConstraintPrefix);
66-
});
61+
#if NET8_0_OR_GREATER
62+
// Prefer AddRoutingCore when available.
63+
// AddRoutingCore doesn't register a regex constraint and produces smaller result from trimming.
64+
services.AddRoutingCore();
65+
services.Configure<RouteOptions>(ConfigureRouting);
66+
#else
67+
services.AddRouting(ConfigureRouting);
68+
#endif
6769
services.AddOptions();
6870
services.TryAddSingleton<GrpcMarkerService>();
6971
services.TryAddSingleton(typeof(ServerCallHandlerFactory<>));
@@ -78,6 +80,13 @@ public static IGrpcServerBuilder AddGrpc(this IServiceCollection services)
7880

7981
return new GrpcServerBuilder(services);
8082

83+
static void ConfigureRouting(RouteOptions options)
84+
{
85+
// Unimplemented constraint is added to the route as an inline constraint to avoid RoutePatternFactory.Parse overload that includes parameter policies. That overload infers strings as regex constraints, which brings in
86+
// the regex engine when publishing trimmed or AOT apps. This change reduces Native AOT gRPC server app size by about 1 MB.
87+
AddParameterPolicy<GrpcUnimplementedConstraint>(options, GrpcServerConstants.GrpcUnimplementedConstraintPrefix);
88+
}
89+
8190
// This ensures the policy's constructors are preserved in .NET 6 with trimming. Remove when .NET 6 is no longer supported.
8291
static void AddParameterPolicy<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(RouteOptions options, string name)
8392
where T : IParameterPolicy

testassets/LinkerTestsClient/LinkerTestsClient.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
<OutputType>Exe</OutputType>
66
<PublishTrimmed>true</PublishTrimmed>
77
<PublishAot>$(AppPublishAot)</PublishAot>
8+
9+
<IlcGenerateMstatFile>$(GenerateAotDiaganostics)</IlcGenerateMstatFile>
10+
<IlcGenerateDgmlFile>$(GenerateAotDiaganostics)</IlcGenerateDgmlFile>
811
</PropertyGroup>
912

1013
<ItemGroup>

testassets/LinkerTestsClient/Program.cs

Lines changed: 47 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -22,72 +22,65 @@
2222
using Microsoft.Extensions.DependencyInjection;
2323
using Unimplemented;
2424

25-
namespace Client;
26-
2725
// This app tests clients created directly from channel and clients created from factory.
2826
// Because of the vagaries of trimming, there is a small chance that testing both in the same app could
2927
// cause them to work when alone they might fail. Consider splitting into different client apps.
30-
public class Program
28+
29+
try
3130
{
32-
static async Task<int> Main(string[] args)
31+
if (args.Length != 1 || !int.TryParse(args[0], out var port))
3332
{
34-
try
35-
{
36-
if (args.Length != 1 || !int.TryParse(args[0], out var port))
37-
{
38-
throw new Exception("Port must be passed as an argument.");
39-
}
33+
throw new Exception("Port must be passed as an argument.");
34+
}
4035

41-
var address = new Uri($"http://localhost:{port}");
36+
var address = new Uri($"http://localhost:{port}");
4237

43-
// Basic channel
44-
using var channel = GrpcChannel.ForAddress(address);
45-
await CallGreeter(new Greeter.GreeterClient(channel));
46-
await CallUnimplemented(new UnimplementedService.UnimplementedServiceClient(channel));
38+
// Basic channel
39+
using var channel = GrpcChannel.ForAddress(address);
40+
await CallGreeter(new Greeter.GreeterClient(channel));
41+
await CallUnimplemented(new UnimplementedService.UnimplementedServiceClient(channel));
4742

48-
// Client factory
49-
var services = new ServiceCollection();
50-
services.AddGrpcClient<Greeter.GreeterClient>(op =>
51-
{
52-
op.Address = address;
53-
});
54-
services.AddGrpcClient<UnimplementedService.UnimplementedServiceClient>(op =>
55-
{
56-
op.Address = address;
57-
});
58-
var serviceProvider = services.BuildServiceProvider();
59-
60-
await CallGreeter(serviceProvider.GetRequiredService<Greeter.GreeterClient>());
61-
await CallUnimplemented(serviceProvider.GetRequiredService<UnimplementedService.UnimplementedServiceClient>());
43+
// Client factory
44+
var services = new ServiceCollection();
45+
services.AddGrpcClient<Greeter.GreeterClient>(op =>
46+
{
47+
op.Address = address;
48+
});
49+
services.AddGrpcClient<UnimplementedService.UnimplementedServiceClient>(op =>
50+
{
51+
op.Address = address;
52+
});
53+
var serviceProvider = services.BuildServiceProvider();
6254

63-
Console.WriteLine("Shutting down");
64-
return 0;
65-
}
66-
catch (Exception ex)
67-
{
68-
Console.Error.WriteLine(ex.ToString());
69-
return 1;
70-
}
71-
}
55+
await CallGreeter(serviceProvider.GetRequiredService<Greeter.GreeterClient>());
56+
await CallUnimplemented(serviceProvider.GetRequiredService<UnimplementedService.UnimplementedServiceClient>());
7257

73-
private static async Task CallGreeter(Greeter.GreeterClient client)
58+
Console.WriteLine("Shutting down");
59+
return 0;
60+
}
61+
catch (Exception ex)
62+
{
63+
Console.Error.WriteLine(ex.ToString());
64+
return 1;
65+
}
66+
67+
static async Task CallGreeter(Greeter.GreeterClient client)
68+
{
69+
var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
70+
Console.WriteLine("Greeting: " + reply.Message);
71+
}
72+
73+
static async Task CallUnimplemented(UnimplementedService.UnimplementedServiceClient client)
74+
{
75+
var reply = client.DuplexData();
76+
77+
try
7478
{
75-
var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
76-
Console.WriteLine("Greeting: " + reply.Message);
79+
await reply.ResponseStream.MoveNext();
80+
throw new Exception("Expected error status.");
7781
}
78-
79-
private static async Task CallUnimplemented(UnimplementedService.UnimplementedServiceClient client)
82+
catch (RpcException ex) when (ex.StatusCode == StatusCode.Unimplemented)
8083
{
81-
var reply = client.DuplexData();
82-
83-
try
84-
{
85-
await reply.ResponseStream.MoveNext();
86-
throw new Exception("Expected error status.");
87-
}
88-
catch (RpcException ex) when (ex.StatusCode == StatusCode.Unimplemented)
89-
{
90-
Console.WriteLine("Unimplemented status correctly returned.");
91-
}
84+
Console.WriteLine("Unimplemented status correctly returned.");
9285
}
9386
}

testassets/LinkerTestsWebsite/LinkerTestsWebsite.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
-->
1212
<TrimMode>full</TrimMode>
1313
<TrimmerSingleWarn>false</TrimmerSingleWarn>
14+
15+
<IlcGenerateMstatFile>$(GenerateAotDiaganostics)</IlcGenerateMstatFile>
16+
<IlcGenerateDgmlFile>$(GenerateAotDiaganostics)</IlcGenerateDgmlFile>
1417
</PropertyGroup>
1518

1619
<ItemGroup>

testassets/LinkerTestsWebsite/Program.cs

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,23 @@
1717
#endregion
1818

1919
using Microsoft.AspNetCore.Server.Kestrel.Core;
20+
using Server;
2021

21-
namespace Server;
22-
23-
public class Program
22+
var builder = WebApplication.CreateSlimBuilder(args);
23+
builder.Logging.SetMinimumLevel(LogLevel.Trace);
24+
builder.WebHost.ConfigureKestrel(options =>
2425
{
25-
public static void Main(string[] args)
26+
options.ListenAnyIP(0, listenOptions =>
2627
{
27-
CreateHostBuilder(args).Build().Run();
28-
}
29-
30-
public static IHostBuilder CreateHostBuilder(string[] args) =>
31-
Host.CreateDefaultBuilder(args)
32-
.ConfigureWebHostDefaults(webBuilder =>
33-
{
34-
webBuilder.UseStartup<Startup>();
35-
webBuilder.ConfigureKestrel(options =>
36-
{
37-
options.ListenAnyIP(0, listenOptions =>
38-
{
39-
listenOptions.Protocols = HttpProtocols.Http2;
40-
});
41-
});
42-
})
43-
.ConfigureLogging(logging =>
44-
{
45-
logging.SetMinimumLevel(LogLevel.Trace);
46-
});
47-
}
28+
listenOptions.Protocols = HttpProtocols.Http2;
29+
});
30+
});
31+
32+
builder.Services.AddGrpc();
33+
34+
var app = builder.Build();
35+
36+
app.MapGrpcService<GreeterService>();
37+
38+
app.Lifetime.ApplicationStarted.Register(() => Console.WriteLine("Application started. Press Ctrl+C to shut down."));
39+
app.Run();

testassets/LinkerTestsWebsite/Startup.cs

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)