public static async Task HandleGrpcRequestAsync(this DownstreamContext context, Func <Task> next, SslCredentials secureCredentials = null) { // ignore if the request is not a gRPC content type if (!context.HttpContext.Request.Headers.Any(h => h.Key.ToLowerInvariant() == "content-type" && h.Value == "application/grpc")) { await next.Invoke(); } else { var methodPath = context.DownstreamReRoute.DownstreamPathTemplate.Value; var grpcAssemblyResolver = context.HttpContext.RequestServices.GetService <GrpcAssemblyResolver>(); var methodDescriptor = grpcAssemblyResolver.FindMethodDescriptor(methodPath.Split('/').Last().ToUpperInvariant()); if (methodDescriptor == null) { await next.Invoke(); } else { string requestData; var upstreamHeaders = new Dictionary <string, string> { { "x-grpc-route-data", JsonConvert.SerializeObject(context.TemplatePlaceholderNameAndValues.Select(x => new { x.Name, x.Value })) }, { "x-grpc-body-data", await context.DownstreamRequest.Content.ReadAsStringAsync() } }; if (context.HttpContext.Request.Method.ToLowerInvariant() == "get") { requestData = context.HttpContext.ParseGetJsonRequest(upstreamHeaders); } else { requestData = context.HttpContext.ParseOtherJsonRequest(upstreamHeaders); } var loadBalancerFactory = context.HttpContext.RequestServices.GetService <ILoadBalancerFactory>(); var loadBalancerResponse = await loadBalancerFactory.Get(context.DownstreamReRoute, context.Configuration.ServiceProviderConfiguration); var serviceHostPort = await loadBalancerResponse.Data.Lease(context); var downstreamAddress = context.DownstreamReRoute.DownstreamAddresses.FirstOrDefault(); var downstreamHost = $"{serviceHostPort.Data.DownstreamHost}:{serviceHostPort.Data.DownstreamPort}"; var channel = new Channel(downstreamHost, secureCredentials ?? ChannelCredentials.Insecure); var client = new MethodDescriptorCaller(channel); var requestObject = JsonConvert.DeserializeObject(requestData, methodDescriptor.InputType.ClrType); var result = await client.InvokeAsync(methodDescriptor, context.HttpContext.GetRequestHeaders(), requestObject); var response = new OkResponse <GrpcHttpContent>(new GrpcHttpContent(JsonConvert.SerializeObject(result))); var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) { Content = response.Data }; context.HttpContext.Response.ContentType = "application/json"; context.DownstreamResponse = new DownstreamResponse(httpResponseMessage); } } }
public static async Task HandleGrpcRequestAsync(this DownstreamContext context, Func <Task> next) { // ignore if the request is not a gRPC content type if (!context.HttpContext.Request.Headers.Any(h => h.Key.ToLowerInvariant() == "content-type" && h.Value == "application/grpc")) { await next.Invoke(); } else { var methodPath = context.DownstreamReRoute.DownstreamPathTemplate.Value; var grpcAssemblyResolver = context.HttpContext.RequestServices.GetService <GrpcAssemblyResolver>(); var methodDescriptor = grpcAssemblyResolver.FindMethodDescriptor(methodPath.Split('/').Last().ToUpperInvariant()); if (methodDescriptor == null) { await next.Invoke(); } else { string requestData; var upstreamHeaders = new Dictionary <string, string> { { "x-grpc-route-data", JsonConvert.SerializeObject(context.TemplatePlaceholderNameAndValues.Select(x => new { x.Name, x.Value })) }, { "x-grpc-body-data", await context.DownstreamRequest.Content.ReadAsStringAsync() } }; if (context.HttpContext.Request.Method.ToLowerInvariant() == "get") { requestData = context.HttpContext.ParseGetJsonRequest(upstreamHeaders); } else { requestData = context.HttpContext.ParseOtherJsonRequest(upstreamHeaders); } // todo: only get the first one, currently we use service mesh to LB the downstream gRPC services // but we open to support it with manually LB such as Consult var downstreamAddress = context.DownstreamReRoute.DownstreamAddresses.FirstOrDefault(); var downstreamHost = $"{downstreamAddress.Host}:{downstreamAddress.Port}"; var channel = new Channel(downstreamHost, ChannelCredentials.Insecure); //todo: handle TLS with certs later var client = new MethodDescriptorCaller(channel); var requestObject = JsonConvert.DeserializeObject(requestData, methodDescriptor.InputType.ClrType); var result = await client.InvokeAsync(methodDescriptor, context.HttpContext.GetRequestHeaders(), requestObject); var response = new OkResponse <GrpcHttpContent>(new GrpcHttpContent(JsonConvert.SerializeObject(result))); var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) { Content = response.Data }; context.HttpContext.Response.ContentType = "application/json"; context.DownstreamResponse = new DownstreamResponse(httpResponseMessage); } } }
public async Task Invoke(HttpContext context, GrpcAssemblyResolver grpcAssemblyResolver, IOptions <GrpcMapperOptions> options) { if (!context.Request.Headers.Any(h => h.Key.ToLowerInvariant() == "content-type" && h.Value == "application/grpc")) { await _next(context); } else { var path = context.Request.Path.Value; var methodDescriptor = grpcAssemblyResolver.FindMethodDescriptor(path.Split('/').Last().ToUpperInvariant()); if (methodDescriptor == null) { await _next(context); } else { string requestData; if (context.Request.Method.ToLowerInvariant() == "get") { requestData = context.ParseGetJsonRequestOnAggregateService(); } else { requestData = await context.ParseOtherJsonRequestOnAggregateService(); } var grpcLookupTable = options.Value.GrpcMappers; var grpcClient = grpcLookupTable.FirstOrDefault(x => x.GrpcMethod == path).GrpcHost; //todo: should catch object to throw exception var channel = new Channel(grpcClient, ChannelCredentials.Insecure); var client = new MethodDescriptorCaller(channel); var requestObject = JsonConvert.DeserializeObject(requestData, methodDescriptor.InputType.ClrType); var result = await client.InvokeAsync(methodDescriptor, context.GetRequestHeaders(), requestObject); await context.Response.WriteAsync(JsonConvert.SerializeObject(result)); } } }
public static async Task HandleGrpcRequestAsync(this DownstreamContext context, Func <Task> next, IEnumerable <Interceptor> interceptors = null, SslCredentials secureCredentials = null) { // ignore if the request is not a gRPC content type if (!context.HttpContext.Request.Headers.Any(h => h.Key.ToLowerInvariant() == "content-type" && h.Value == "application/grpc")) { await next.Invoke(); } else { var methodPath = context.DownstreamReRoute.DownstreamPathTemplate.Value; var grpcAssemblyResolver = context.HttpContext.RequestServices.GetService <GrpcAssemblyResolver>(); var methodDescriptor = grpcAssemblyResolver.FindMethodDescriptor(methodPath.Split('/').Last().ToUpperInvariant()); if (methodDescriptor == null) { await next.Invoke(); } else { var logger = context.HttpContext.RequestServices.GetService <IOcelotLoggerFactory>().CreateLogger <GrpcAssemblyResolver>(); var upstreamHeaders = new Dictionary <string, string> { { "x-grpc-route-data", JsonConvert.SerializeObject(context.TemplatePlaceholderNameAndValues.Select(x => new { x.Name, x.Value })) }, { "x-grpc-body-data", await context.DownstreamRequest.Content.ReadAsStringAsync() } }; logger.LogInformation($"Upstream request method is {context.HttpContext.Request.Method}"); logger.LogInformation($"Upstream header data for x-grpc-route-data is {upstreamHeaders["x-grpc-route-data"]}"); logger.LogInformation($"Upstream header data for x-grpc-body-data is {upstreamHeaders["x-grpc-body-data"]}"); var requestObject = context.HttpContext.ParseRequestData(upstreamHeaders); var requestJsonData = JsonConvert.SerializeObject(requestObject); logger.LogInformation($"Request object data is {requestJsonData}"); var loadBalancerFactory = context.HttpContext.RequestServices.GetService <ILoadBalancerFactory>(); var loadBalancerResponse = await loadBalancerFactory.Get(context.DownstreamReRoute, context.Configuration.ServiceProviderConfiguration); var serviceHostPort = await loadBalancerResponse.Data.Lease(context); var downstreamHost = $"{serviceHostPort.Data.DownstreamHost}:{serviceHostPort.Data.DownstreamPort}"; logger.LogInformation($"Downstream IP Address is {downstreamHost}"); var channel = new Channel(downstreamHost, secureCredentials ?? ChannelCredentials.Insecure); MethodDescriptorCaller client; if (interceptors != null && interceptors.Any()) { CallInvoker callInvoker = null; foreach (var interceptor in interceptors) { callInvoker = channel.Intercept(interceptor); } client = new MethodDescriptorCaller(callInvoker); } else { client = new MethodDescriptorCaller(channel); } var concreteObject = JsonConvert.DeserializeObject(requestJsonData, methodDescriptor.InputType.ClrType); var result = await client.InvokeAsync(methodDescriptor, context.HttpContext.GetRequestHeaders(), concreteObject); logger.LogDebug($"gRPC response called with {JsonConvert.SerializeObject(result)}"); var jsonSerializer = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }; var response = new OkResponse <GrpcHttpContent>(new GrpcHttpContent(JsonConvert.SerializeObject(result, jsonSerializer))); var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) { Content = response.Data }; context.HttpContext.Response.ContentType = "application/json"; context.DownstreamResponse = new DownstreamResponse(httpResponseMessage); } } }