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; } }
internal async Task ProcessHandlerErrorAsync(Exception ex, string method, bool isStreaming, JsonSerializerOptions options) { Status status; if (ex is RpcException rpcException) { // RpcException is thrown by client code to modify the status returned from the server. // Log the status and detail. Don't log the exception to reduce log verbosity. GrpcServerLog.RpcConnectionError(Logger, rpcException.StatusCode, rpcException.Status.Detail); status = rpcException.Status; } else { GrpcServerLog.ErrorExecutingServiceMethod(Logger, method, ex); var message = ErrorMessageHelper.BuildErrorMessage("Exception was thrown by handler.", ex, Options.EnableDetailedErrors); // Note that the exception given to status won't be returned to the client. // It is still useful to set in case an interceptor accesses the status on the server. status = new Status(StatusCode.Unknown, message, ex); } await JsonRequestHelpers.SendErrorResponse(HttpContext.Response, RequestEncoding, status, options); if (isStreaming) { await HttpContext.Response.Body.WriteAsync(GrpcProtocolConstants.StreamingDelimiter); } }
public Task HandleCallAsync(HttpContext httpContext) { if (GrpcProtocolHelpers.IsInvalidContentType(httpContext, out var error)) { // This might be a CORS preflight request and CORS middleware hasn't been configured if (HttpMethods.IsOptions(httpContext.Request.Method)) { GrpcServerLog.UnhandledCorsPreflightRequest(Logger); GrpcProtocolHelpers.BuildHttpErrorResponse(httpContext.Response, StatusCodes.Status405MethodNotAllowed, StatusCode.Internal, "Unhandled CORS preflight request received. CORS may not be configured correctly in the application."); httpContext.Response.Headers[HeaderNames.Allow] = HttpMethods.Post; return(Task.CompletedTask); } else { GrpcServerLog.UnsupportedRequestContentType(Logger, httpContext.Request.ContentType); GrpcProtocolHelpers.BuildHttpErrorResponse(httpContext.Response, StatusCodes.Status415UnsupportedMediaType, StatusCode.Internal, error); return(Task.CompletedTask); } } if (httpContext.Request.Protocol != GrpcProtocolConstants.Http2Protocol && httpContext.Request.Protocol != GrpcProtocolConstants.Http20Protocol) { GrpcServerLog.UnsupportedRequestProtocol(Logger, httpContext.Request.Protocol); var protocolError = $"Request protocol '{httpContext.Request.Protocol}' is not supported."; GrpcProtocolHelpers.BuildHttpErrorResponse(httpContext.Response, StatusCodes.Status426UpgradeRequired, StatusCode.Internal, protocolError); httpContext.Response.Headers[HeaderNames.Upgrade] = GrpcProtocolConstants.Http2Protocol; return(Task.CompletedTask); } var serverCallContext = new HttpContextServerCallContext(httpContext, MethodInvoker.Options, typeof(TRequest), typeof(TResponse), Logger); httpContext.Features.Set <IServerCallContextFeature>(serverCallContext); GrpcProtocolHelpers.AddProtocolHeaders(httpContext.Response); try { serverCallContext.Initialize(); var handleCallTask = HandleCallAsyncCore(httpContext, serverCallContext); if (handleCallTask.IsCompletedSuccessfully) { return(serverCallContext.EndCallAsync()); } else { return(AwaitHandleCall(serverCallContext, MethodInvoker.Method, handleCallTask)); } } catch (Exception ex) { return(serverCallContext.ProcessHandlerErrorAsync(ex, MethodInvoker.Method.Name)); }
public Task HandleCallAsync(HttpContext httpContext) { if (GrpcProtocolHelpers.IsInvalidContentType(httpContext, out var error)) { GrpcServerLog.UnsupportedRequestContentType(Logger, httpContext.Request.ContentType); GrpcProtocolHelpers.BuildHttpErrorResponse(httpContext.Response, StatusCodes.Status415UnsupportedMediaType, StatusCode.Internal, error); return(Task.CompletedTask); } if (httpContext.Request.Protocol != GrpcProtocolConstants.Http2Protocol && httpContext.Request.Protocol != GrpcProtocolConstants.Http20Protocol) { GrpcServerLog.UnsupportedRequestProtocol(Logger, httpContext.Request.Protocol); var protocolError = $"Request protocol '{httpContext.Request.Protocol}' is not supported."; GrpcProtocolHelpers.BuildHttpErrorResponse(httpContext.Response, StatusCodes.Status426UpgradeRequired, StatusCode.Internal, protocolError); httpContext.Response.Headers[HeaderNames.Upgrade] = GrpcProtocolConstants.Http2Protocol; return(Task.CompletedTask); } var serverCallContext = new HttpContextServerCallContext(httpContext, ServiceOptions, Logger); httpContext.Features.Set <IServerCallContextFeature>(serverCallContext); GrpcProtocolHelpers.AddProtocolHeaders(httpContext.Response); try { serverCallContext.Initialize(); var handleCallTask = HandleCallAsyncCore(httpContext, serverCallContext); if (handleCallTask.IsCompletedSuccessfully) { return(serverCallContext.EndCallAsync()); } else { return(AwaitHandleCall(serverCallContext, Method, handleCallTask)); } } catch (Exception ex) { return(serverCallContext.ProcessHandlerErrorAsync(ex, Method.Name)); }
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)); } }