// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Server.Kestrel.Core { public sealed class BadHttpRequestException : IOException { private BadHttpRequestException(string message, int statusCode, RequestRejectionReason reason) : this(message, statusCode, reason, null) { } private BadHttpRequestException(string message, int statusCode, RequestRejectionReason reason, HttpMethod? requiredMethod) : base(message) { StatusCode = statusCode; Reason = reason; if (requiredMethod.HasValue) { AllowedHeader = HttpUtilities.MethodToString(requiredMethod.Value); } } internal int StatusCode { get; } internal StringValues AllowedHeader { get; } internal RequestRejectionReason Reason { get; } [StackTraceHidden] internal static void Throw(RequestRejectionReason reason) { throw GetException(reason); } [StackTraceHidden] #pragma warning disable PUB0001 // Pubternal type in public API public static void Throw(RequestRejectionReason reason, HttpMethod method) => throw GetException(reason, method.ToString().ToUpperInvariant()); #pragma warning restore PUB0001 // Pubternal type in public API [MethodImpl(MethodImplOptions.NoInlining)] internal static BadHttpRequestException GetException(RequestRejectionReason reason) { BadHttpRequestException ex; switch (reason) { case RequestRejectionReason.InvalidRequestHeadersNoCRLF: ex = new BadHttpRequestException(CoreStrings.BadRequest_InvalidRequestHeadersNoCRLF, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.InvalidRequestLine: ex = new BadHttpRequestException(CoreStrings.BadRequest_InvalidRequestLine, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.MalformedRequestInvalidHeaders: ex = new BadHttpRequestException(CoreStrings.BadRequest_MalformedRequestInvalidHeaders, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.MultipleContentLengths: ex = new BadHttpRequestException(CoreStrings.BadRequest_MultipleContentLengths, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.UnexpectedEndOfRequestContent: ex = new BadHttpRequestException(CoreStrings.BadRequest_UnexpectedEndOfRequestContent, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.BadChunkSuffix: ex = new BadHttpRequestException(CoreStrings.BadRequest_BadChunkSuffix, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.BadChunkSizeData: ex = new BadHttpRequestException(CoreStrings.BadRequest_BadChunkSizeData, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.ChunkedRequestIncomplete: ex = new BadHttpRequestException(CoreStrings.BadRequest_ChunkedRequestIncomplete, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.InvalidCharactersInHeaderName: ex = new BadHttpRequestException(CoreStrings.BadRequest_InvalidCharactersInHeaderName, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.RequestLineTooLong: ex = new BadHttpRequestException(CoreStrings.BadRequest_RequestLineTooLong, StatusCodes.Status414UriTooLong, reason); break; case RequestRejectionReason.HeadersExceedMaxTotalSize: ex = new BadHttpRequestException(CoreStrings.BadRequest_HeadersExceedMaxTotalSize, StatusCodes.Status431RequestHeaderFieldsTooLarge, reason); break; case RequestRejectionReason.TooManyHeaders: ex = new BadHttpRequestException(CoreStrings.BadRequest_TooManyHeaders, StatusCodes.Status431RequestHeaderFieldsTooLarge, reason); break; case RequestRejectionReason.RequestBodyTooLarge: ex = new BadHttpRequestException(CoreStrings.BadRequest_RequestBodyTooLarge, StatusCodes.Status413PayloadTooLarge, reason); break; case RequestRejectionReason.RequestHeadersTimeout: ex = new BadHttpRequestException(CoreStrings.BadRequest_RequestHeadersTimeout, StatusCodes.Status408RequestTimeout, reason); break; case RequestRejectionReason.RequestBodyTimeout: ex = new BadHttpRequestException(CoreStrings.BadRequest_RequestBodyTimeout, StatusCodes.Status408RequestTimeout, reason); break; case RequestRejectionReason.OptionsMethodRequired: ex = new BadHttpRequestException(CoreStrings.BadRequest_MethodNotAllowed, StatusCodes.Status405MethodNotAllowed, reason, HttpMethod.Options); break; case RequestRejectionReason.ConnectMethodRequired: ex = new BadHttpRequestException(CoreStrings.BadRequest_MethodNotAllowed, StatusCodes.Status405MethodNotAllowed, reason, HttpMethod.Connect); break; case RequestRejectionReason.MissingHostHeader: ex = new BadHttpRequestException(CoreStrings.BadRequest_MissingHostHeader, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.MultipleHostHeaders: ex = new BadHttpRequestException(CoreStrings.BadRequest_MultipleHostHeaders, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.InvalidHostHeader: ex = new BadHttpRequestException(CoreStrings.BadRequest_InvalidHostHeader, StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.UpgradeRequestCannotHavePayload: ex = new BadHttpRequestException(CoreStrings.BadRequest_UpgradeRequestCannotHavePayload, StatusCodes.Status400BadRequest, reason); break; default: ex = new BadHttpRequestException(CoreStrings.BadRequest, StatusCodes.Status400BadRequest, reason); break; } return ex; } [StackTraceHidden] internal static void Throw(RequestRejectionReason reason, string detail) { throw GetException(reason, detail); } [StackTraceHidden] internal static void Throw(RequestRejectionReason reason, in StringValues detail) { throw GetException(reason, detail.ToString()); } [MethodImpl(MethodImplOptions.NoInlining)] internal static BadHttpRequestException GetException(RequestRejectionReason reason, string detail) { BadHttpRequestException ex; switch (reason) { case RequestRejectionReason.InvalidRequestLine: ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_InvalidRequestLine_Detail(detail), StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.InvalidRequestTarget: ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_InvalidRequestTarget_Detail(detail), StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.InvalidRequestHeader: ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(detail), StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.InvalidContentLength: ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_InvalidContentLength_Detail(detail), StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.UnrecognizedHTTPVersion: ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_UnrecognizedHTTPVersion(detail), StatusCodes.Status505HttpVersionNotsupported, reason); break; case RequestRejectionReason.FinalTransferCodingNotChunked: ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_FinalTransferCodingNotChunked(detail), StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.LengthRequired: ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_LengthRequired(detail), StatusCodes.Status411LengthRequired, reason); break; case RequestRejectionReason.LengthRequiredHttp10: ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_LengthRequiredHttp10(detail), StatusCodes.Status400BadRequest, reason); break; case RequestRejectionReason.InvalidHostHeader: ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_InvalidHostHeader_Detail(detail), StatusCodes.Status400BadRequest, reason); break; default: ex = new BadHttpRequestException(CoreStrings.BadRequest, StatusCodes.Status400BadRequest, reason); break; } return ex; } } }