/// <summary> /// Sends an HTTP request to the inner handler to send to the server as an asynchronous operation. /// </summary> /// <param name="request">The HTTP request message to send to the server.</param> /// <param name="cancellationToken">A cancellation token to cancel operation.</param> /// <returns>The task object representing the asynchronous operation.</returns> protected override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (CommonGrpcProtocolHelpers.IsContentType(GrpcWebProtocolConstants.GrpcContentType, request.Content?.Headers.ContentType?.MediaType)) { return(SendAsyncCore(request, cancellationToken)); } return(base.SendAsync(request, cancellationToken)); }
private static bool IsMatchingResponseContentType(GrpcWebMode mode, string?contentType) { if (mode == Web.GrpcWebMode.GrpcWeb) { return(CommonGrpcProtocolHelpers.IsContentType(GrpcWebProtocolConstants.GrpcWebContentType, contentType)); } if (mode == Web.GrpcWebMode.GrpcWebText) { return(CommonGrpcProtocolHelpers.IsContentType(GrpcWebProtocolConstants.GrpcWebTextContentType, contentType)); } return(false); }
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { if (httpContext == null) { return(false); } if (!HttpMethods.IsPost(httpContext.Request.Method)) { return(false); } return(CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcContentType, httpContext.Request.ContentType) || CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcWebContentType, httpContext.Request.ContentType) || CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcWebTextContentType, httpContext.Request.ContentType)); }
internal static ServerGrpcWebMode GetGrpcWebMode(HttpContext httpContext) { if (HttpMethods.IsPost(httpContext.Request.Method)) { if (CommonGrpcProtocolHelpers.IsContentType(GrpcWebProtocolConstants.GrpcWebContentType, httpContext.Request.ContentType)) { return(ServerGrpcWebMode.GrpcWeb); } else if (CommonGrpcProtocolHelpers.IsContentType(GrpcWebProtocolConstants.GrpcWebTextContentType, httpContext.Request.ContentType)) { return(ServerGrpcWebMode.GrpcWebText); } } return(ServerGrpcWebMode.None); }
public static bool IsInvalidContentType(HttpContext httpContext, [NotNullWhen(true)] out string?error) { if (httpContext.Request.ContentType == null) { error = "Content-Type is missing from the request."; return(true); } else if (!CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcContentType, httpContext.Request.ContentType)) { error = $"Content-Type '{httpContext.Request.ContentType}' is not supported."; return(true); } error = null; return(false); }
private async Task HandleGrpcWebRequest(HttpContext httpContext, ServerGrpcWebMode mode) { var feature = new GrpcWebFeature(mode, httpContext); var initialProtocol = httpContext.Request.Protocol; // Modifying the request is required to stop Grpc.AspNetCore.Server from rejecting it httpContext.Request.Protocol = GrpcWebProtocolConstants.Http2Protocol; httpContext.Request.ContentType = ResolveContentType(GrpcWebProtocolConstants.GrpcContentType, httpContext.Request.ContentType); // Update response content type back to gRPC-Web httpContext.Response.OnStarting(() => { // Reset request protocol back to its original value. Not doing this causes a 2 second // delay when making HTTP/1.1 calls. httpContext.Request.Protocol = initialProtocol; if (CommonGrpcProtocolHelpers.IsContentType(GrpcWebProtocolConstants.GrpcContentType, httpContext.Response.ContentType)) { var contentType = mode == ServerGrpcWebMode.GrpcWeb ? GrpcWebProtocolConstants.GrpcWebContentType : GrpcWebProtocolConstants.GrpcWebTextContentType; var responseContentType = ResolveContentType(contentType, httpContext.Response.ContentType); httpContext.Response.ContentType = responseContentType; Log.SendingGrpcWebResponse(_logger, responseContentType); } return(Task.CompletedTask); }); try { await _next(httpContext); // If trailers have already been written in CompleteAsync then this will no-op await feature.WriteTrailersAsync(); } finally { feature.DetachFromContext(httpContext); } }
internal static Status?ValidateHeaders(HttpResponseMessage httpResponse, out Metadata?trailers) { // gRPC status can be returned in the header when there is no message (e.g. unimplemented status) // An explicitly specified status header has priority over other failing statuses if (GrpcProtocolHelpers.TryGetStatusCore(httpResponse.Headers, out var status)) { // Trailers are in the header because there is no message. // Note that some default headers will end up in the trailers (e.g. Date, Server). trailers = GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers); return(status); } trailers = null; // ALPN negotiation is sending HTTP/1.1 and HTTP/2. // Check that the response wasn't downgraded to HTTP/1.1. if (httpResponse.Version < GrpcProtocolConstants.Http2Version) { return(new Status(StatusCode.Internal, $"Bad gRPC response. Response protocol downgraded to HTTP/{httpResponse.Version.ToString(2)}.")); } if (httpResponse.StatusCode != HttpStatusCode.OK) { var statusCode = MapHttpStatusToGrpcCode(httpResponse.StatusCode); return(new Status(statusCode, "Bad gRPC response. HTTP status code: " + (int)httpResponse.StatusCode)); } // Don't access Headers.ContentType property because it is not threadsafe. var contentType = GrpcProtocolHelpers.GetHeaderValue(httpResponse.Content?.Headers, "Content-Type"); if (contentType == null) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Response did not have a content-type header.")); } if (!CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcContentType, contentType)) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Invalid content-type value: " + contentType)); } // Call is still in progress return(null); }
private Status?ValidateHeaders(HttpResponseMessage httpResponse) { GrpcCallLog.ResponseHeadersReceived(Logger); // gRPC status can be returned in the header when there is no message (e.g. unimplemented status) // An explicitly specified status header has priority over other failing statuses if (GrpcProtocolHelpers.TryGetStatusCore(httpResponse.Headers, out var status)) { // Trailers are in the header because there is no message. // Note that some default headers will end up in the trailers (e.g. Date, Server). _trailers = GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers); return(status); } // ALPN negotiation is sending HTTP/1.1 and HTTP/2. // Check that the response wasn't downgraded to HTTP/1.1. if (httpResponse.Version < HttpVersion.Version20) { return(new Status(StatusCode.Internal, $"Bad gRPC response. Response protocol downgraded to HTTP/{httpResponse.Version.ToString(2)}.")); } if (httpResponse.StatusCode != HttpStatusCode.OK) { var statusCode = MapHttpStatusToGrpcCode(httpResponse.StatusCode); return(new Status(statusCode, "Bad gRPC response. HTTP status code: " + (int)httpResponse.StatusCode)); } if (httpResponse.Content?.Headers.ContentType == null) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Response did not have a content-type header.")); } var grpcEncoding = httpResponse.Content.Headers.ContentType; if (!CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcContentType, grpcEncoding?.MediaType)) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Invalid content-type value: " + grpcEncoding)); } // Call is still in progress return(null); }
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { if (httpContext == null) { return(false); } // Constraint needs to be valid when a CORS preflight request is received so that CORS middleware will run if (GrpcProtocolHelpers.IsCorsPreflightRequest(httpContext)) { return(true); } if (!HttpMethods.IsPost(httpContext.Request.Method)) { return(false); } return(CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcContentType, httpContext.Request.ContentType) || CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcWebContentType, httpContext.Request.ContentType) || CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcWebTextContentType, httpContext.Request.ContentType)); }