-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGrpcEndpoints.cs
More file actions
103 lines (90 loc) · 3.91 KB
/
Copy pathGrpcEndpoints.cs
File metadata and controls
103 lines (90 loc) · 3.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
namespace Shiny.Net.HttpServer.Grpc;
/// <summary>
/// gRPC and gRPC-Web, served by the same HTTP/2 stack as everything else — no ASP.NET Core, and
/// nothing reflecting over your message types.
/// <para>
/// A gRPC method is a POST to <c>/{service}/{method}</c> whose body is a sequence of length-prefixed
/// messages, and whose outcome arrives in trailers after them. That last part is why this needs a
/// server that can send trailers, and why it works here.
/// </para>
/// <code>
/// app.MapGrpcService("greet.Greeter", svc =>
/// {
/// svc.AddMarshaller<HelloRequest>(m => m.ToByteArray(), HelloRequest.Parser.ParseFrom);
/// svc.AddMarshaller<HelloReply>(m => m.ToByteArray(), HelloReply.Parser.ParseFrom);
///
/// svc.MapUnary<HelloRequest, HelloReply>("SayHello", (request, context) =>
/// new ValueTask<HelloReply>(new HelloReply { Message = $"Hello {request.Name}" }));
///
/// svc.MapServerStreaming<HelloRequest, HelloReply>("Greetings", Greetings);
/// });
/// </code>
/// <para>
/// Callers reach it with <c>Grpc.Net.Client</c>, <c>grpcurl</c>, or any gRPC client in any language.
/// Over cleartext they need HTTP/2 without TLS — <c>options.Http2.AllowCleartext</c> here, and
/// <c>AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true)</c>
/// on a .NET client. Browsers use gRPC-Web, which is on by default and needs a CORS policy that
/// exposes <c>grpc-status</c> and <c>grpc-message</c>.
/// </para>
/// </summary>
public static class GrpcEndpoints
{
/// <summary>Maps a gRPC service and its methods.</summary>
public static GrpcServiceBuilder MapGrpcService(
this HttpServer server,
string serviceName,
Action<GrpcServiceBuilder> configure
)
{
ArgumentNullException.ThrowIfNull(server);
return server.MapGrpcService(serviceName, new GrpcOptions(), configure);
}
/// <summary>Maps a gRPC service from options built elsewhere — shared marshallers, shared limits.</summary>
public static GrpcServiceBuilder MapGrpcService(
this HttpServer server,
string serviceName,
GrpcOptions options,
Action<GrpcServiceBuilder> configure
)
{
ArgumentNullException.ThrowIfNull(server);
GrpcServiceBuilder? builder = null;
server.MapGroup(NormalizeService(serviceName), group => builder = Map(group, serviceName, options, configure));
return builder!;
}
/// <summary>Maps a gRPC service onto an existing route builder, inside whatever prefix it carries.</summary>
public static GrpcServiceBuilder MapGrpcService(
this IEndpointRouteBuilder endpoints,
string serviceName,
Action<GrpcServiceBuilder> configure
) => endpoints.MapGrpcService(serviceName, new GrpcOptions(), configure);
/// <summary>Maps a gRPC service onto an existing route builder, from options built elsewhere.</summary>
public static GrpcServiceBuilder MapGrpcService(
this IEndpointRouteBuilder endpoints,
string serviceName,
GrpcOptions options,
Action<GrpcServiceBuilder> configure
)
{
ArgumentNullException.ThrowIfNull(endpoints);
return Map(endpoints.MapGroup(NormalizeService(serviceName)), serviceName, options, configure);
}
static GrpcServiceBuilder Map(
IEndpointRouteBuilder group,
string serviceName,
GrpcOptions options,
Action<GrpcServiceBuilder> configure
)
{
ArgumentNullException.ThrowIfNull(options);
ArgumentNullException.ThrowIfNull(configure);
var builder = new GrpcServiceBuilder(group, NormalizeService(serviceName).Trim('/'), options);
configure(builder);
return builder;
}
static string NormalizeService(string serviceName)
{
ArgumentException.ThrowIfNullOrWhiteSpace(serviceName);
return "/" + serviceName.Trim().Trim('/');
}
}