Beispiel #1
0
    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);
        }
    }
Beispiel #3
0
        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));
            }
Beispiel #4
0
        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));
            }
Beispiel #5
0
    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));
        }
    }