public static async ValueTask SendMessage <TResponse>(JsonTranscodingServerCallContext serverCallContext, JsonSerializerOptions serializerOptions, TResponse message, CancellationToken cancellationToken) where TResponse : class { var response = serverCallContext.HttpContext.Response; try { GrpcServerLog.SendingMessage(serverCallContext.Logger); object responseBody; Type responseType; if (serverCallContext.DescriptorInfo.ResponseBodyDescriptor != null) { // TODO: Support recursive response body? responseBody = serverCallContext.DescriptorInfo.ResponseBodyDescriptor.Accessor.GetValue((IMessage)message); responseType = JsonConverterHelper.GetFieldType(serverCallContext.DescriptorInfo.ResponseBodyDescriptor); } else { responseBody = message; responseType = message.GetType(); } await JsonRequestHelpers.WriteResponseMessage(response, serverCallContext.RequestEncoding, responseBody, serializerOptions, cancellationToken); GrpcServerLog.SerializedMessage(serverCallContext.Logger, responseType); GrpcServerLog.MessageSent(serverCallContext.Logger); } catch (Exception ex) { GrpcServerLog.ErrorSendingMessage(serverCallContext.Logger, ex); throw; } }
protected override async Task HandleCallAsyncCore(HttpContext httpContext, JsonTranscodingServerCallContext serverCallContext) { var request = await JsonRequestHelpers.ReadMessage <TRequest>(serverCallContext, SerializerOptions); var response = await _invoker.Invoke(httpContext, serverCallContext, request); if (serverCallContext.Status.StatusCode != StatusCode.OK) { throw new RpcException(serverCallContext.Status); } if (response == null) { // This is consistent with Grpc.Core when a null value is returned throw new RpcException(new Status(StatusCode.Cancelled, "No message returned from method.")); } if (response is HttpBody httpBody) { serverCallContext.EnsureResponseHeaders(httpBody.ContentType); await serverCallContext.HttpContext.Response.Body.WriteAsync(httpBody.Data.Memory); } else { serverCallContext.EnsureResponseHeaders(); await JsonRequestHelpers.SendMessage(serverCallContext, SerializerOptions, response, CancellationToken.None); } }
public Task HandleCallAsync(HttpContext httpContext) { var serverCallContext = new JsonTranscodingServerCallContext(httpContext, MethodInvoker.Options, MethodInvoker.Method, DescriptorInfo, Logger); httpContext.Features.Set <IServerCallContextFeature>(serverCallContext); try { serverCallContext.Initialize(); var handleCallTask = HandleCallAsyncCore(httpContext, serverCallContext); if (handleCallTask.IsCompletedSuccessfully) { return(Task.CompletedTask); } else { return(AwaitHandleCall(serverCallContext, MethodInvoker.Method, IsStreaming, SerializerOptions, handleCallTask)); } } catch (Exception ex) { return(serverCallContext.ProcessHandlerErrorAsync(ex, MethodInvoker.Method.Name, IsStreaming, SerializerOptions)); }
private static bool CanBindQueryStringVariable(JsonTranscodingServerCallContext serverCallContext, string variable) { if (serverCallContext.DescriptorInfo.BodyDescriptor != null) { if (serverCallContext.DescriptorInfo.BodyFieldDescriptors == null || serverCallContext.DescriptorInfo.BodyFieldDescriptors.Count == 0) { return(false); } if (variable == serverCallContext.DescriptorInfo.BodyFieldDescriptorsPath) { return(false); } if (variable.StartsWith(serverCallContext.DescriptorInfo.BodyFieldDescriptorsPath !, StringComparison.Ordinal)) { return(false); } } if (serverCallContext.DescriptorInfo.RouteParameterDescriptors.ContainsKey(variable)) { return(false); } return(true); }
private static async ValueTask <byte[]> ReadDataAsync(JsonTranscodingServerCallContext serverCallContext) { // Buffer to disk if content is larger than 30Kb. // Based on value in XmlSerializer and NewtonsoftJson input formatters. const int DefaultMemoryThreshold = 1024 * 30; var memoryThreshold = DefaultMemoryThreshold; var contentLength = serverCallContext.HttpContext.Request.ContentLength.GetValueOrDefault(); if (contentLength > 0 && contentLength < memoryThreshold) { // If the Content-Length is known and is smaller than the default buffer size, use it. memoryThreshold = (int)contentLength; } using var fs = new FileBufferingReadStream(serverCallContext.HttpContext.Request.Body, memoryThreshold); // Read the request body into buffer. // No explicit cancellation token. Request body uses underlying request aborted token. await fs.DrainAsync(CancellationToken.None); fs.Seek(0, SeekOrigin.Begin); var data = new byte[fs.Length]; var read = fs.Read(data); Debug.Assert(read == data.Length); return(data); }
private static List <FieldDescriptor>?GetPathDescriptors(JsonTranscodingServerCallContext serverCallContext, IMessage requestMessage, string path) { return(serverCallContext.DescriptorInfo.PathDescriptorsCache.GetOrAdd(path, p => { ServiceDescriptorHelpers.TryResolveDescriptors(requestMessage.Descriptor, p, out var pathDescriptors); return pathDescriptors; })); }
static async Task AwaitHandleCall(JsonTranscodingServerCallContext serverCallContext, Method <TRequest, TResponse> method, bool isStreaming, JsonSerializerOptions serializerOptions, Task handleCall) { try { await handleCall; } catch (Exception ex) { await serverCallContext.ProcessHandlerErrorAsync(ex, method.Name, isStreaming, serializerOptions); } }
protected override async Task HandleCallAsyncCore(HttpContext httpContext, JsonTranscodingServerCallContext serverCallContext) { // Decode request var request = await JsonRequestHelpers.ReadMessage <TRequest>(serverCallContext, SerializerOptions); var streamWriter = new HttpContextStreamWriter <TResponse>(serverCallContext, SerializerOptions); try { await _invoker.Invoke(httpContext, serverCallContext, request, streamWriter); } finally { streamWriter.Complete(); } }
private static async ValueTask <IMessage> ReadHttpBodyAsync(JsonTranscodingServerCallContext serverCallContext) { var httpBody = (IMessage)Activator.CreateInstance(serverCallContext.DescriptorInfo.BodyDescriptor !.ClrType) !; var contentType = serverCallContext.HttpContext.Request.ContentType; if (contentType != null) { httpBody.Descriptor.Fields[HttpBody.ContentTypeFieldNumber].Accessor.SetValue(httpBody, contentType); } var data = await ReadDataAsync(serverCallContext); httpBody.Descriptor.Fields[HttpBody.DataFieldNumber].Accessor.SetValue(httpBody, UnsafeByteOperations.UnsafeWrap(data)); return(httpBody); }
public HttpContextStreamWriter(JsonTranscodingServerCallContext context, JsonSerializerOptions serializerOptions) { _context = context; _serializerOptions = serializerOptions; _writeLock = new object(); }
public static async ValueTask <TRequest> ReadMessage <TRequest>(JsonTranscodingServerCallContext serverCallContext, JsonSerializerOptions serializerOptions) where TRequest : class { try { GrpcServerLog.ReadingMessage(serverCallContext.Logger); IMessage requestMessage; if (serverCallContext.DescriptorInfo.BodyDescriptor != null) { Type type; object bodyContent; if (serverCallContext.DescriptorInfo.BodyDescriptor.FullName == HttpBody.Descriptor.FullName) { type = typeof(HttpBody); bodyContent = await ReadHttpBodyAsync(serverCallContext); } else { if (!serverCallContext.IsJsonRequestContent) { GrpcServerLog.UnsupportedRequestContentType(serverCallContext.Logger, serverCallContext.HttpContext.Request.ContentType); throw new InvalidOperationException($"Unable to read the request as JSON because the request content type '{serverCallContext.HttpContext.Request.ContentType}' is not a known JSON content type."); } var(stream, usesTranscodingStream) = GetStream(serverCallContext.HttpContext.Request.Body, serverCallContext.RequestEncoding); try { if (serverCallContext.DescriptorInfo.BodyDescriptorRepeated) { requestMessage = (IMessage)Activator.CreateInstance <TRequest>(); // TODO: JsonSerializer currently doesn't support deserializing values onto an existing object or collection. // Either update this to use new functionality in JsonSerializer or improve work-around perf. type = JsonConverterHelper.GetFieldType(serverCallContext.DescriptorInfo.BodyFieldDescriptors.Last()); type = typeof(List <>).MakeGenericType(type); GrpcServerLog.DeserializingMessage(serverCallContext.Logger, type); bodyContent = (await JsonSerializer.DeserializeAsync(stream, type, serializerOptions)) !; if (bodyContent == null) { throw new InvalidOperationException($"Unable to deserialize null to {type.Name}."); } } else { type = serverCallContext.DescriptorInfo.BodyDescriptor.ClrType; GrpcServerLog.DeserializingMessage(serverCallContext.Logger, type); bodyContent = (IMessage)(await JsonSerializer.DeserializeAsync(stream, serverCallContext.DescriptorInfo.BodyDescriptor.ClrType, serializerOptions)) !; } } finally { if (usesTranscodingStream) { await stream.DisposeAsync(); } } } if (serverCallContext.DescriptorInfo.BodyFieldDescriptors != null) { requestMessage = (IMessage)Activator.CreateInstance <TRequest>(); ServiceDescriptorHelpers.RecursiveSetValue(requestMessage, serverCallContext.DescriptorInfo.BodyFieldDescriptors, bodyContent); // TODO - check nullability } else { if (bodyContent == null) { throw new InvalidOperationException($"Unable to deserialize null to {type.Name}."); } requestMessage = (IMessage)bodyContent; } } else { requestMessage = (IMessage)Activator.CreateInstance <TRequest>(); } foreach (var parameterDescriptor in serverCallContext.DescriptorInfo.RouteParameterDescriptors) { var routeValue = serverCallContext.HttpContext.Request.RouteValues[parameterDescriptor.Key]; if (routeValue != null) { ServiceDescriptorHelpers.RecursiveSetValue(requestMessage, parameterDescriptor.Value, routeValue); } } foreach (var item in serverCallContext.HttpContext.Request.Query) { if (CanBindQueryStringVariable(serverCallContext, item.Key)) { var pathDescriptors = GetPathDescriptors(serverCallContext, requestMessage, item.Key); if (pathDescriptors != null) { var value = item.Value.Count == 1 ? (object?)item.Value[0] : item.Value; ServiceDescriptorHelpers.RecursiveSetValue(requestMessage, pathDescriptors, value); } } } GrpcServerLog.ReceivedMessage(serverCallContext.Logger); return((TRequest)requestMessage); } catch (JsonException ex) { GrpcServerLog.ErrorReadingMessage(serverCallContext.Logger, ex); throw new RpcException(new Status(StatusCode.InvalidArgument, "Request JSON payload is not correctly formatted.", ex)); } catch (Exception ex) { GrpcServerLog.ErrorReadingMessage(serverCallContext.Logger, ex); throw new RpcException(new Status(StatusCode.InvalidArgument, ex.Message, ex)); } }