Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Commit

Permalink
Consolidate Exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
benaadams committed Jul 19, 2016
1 parent bdf9f8d commit 6b161f5
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,108 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.IO;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;

namespace Microsoft.AspNetCore.Server.Kestrel
{
public sealed class BadHttpRequestException : IOException
{
internal BadHttpRequestException(string message)
private BadHttpRequestException(string message)
: base(message)
{

}

[MethodImpl(MethodImplOptions.NoInlining)]
internal static BadHttpRequestException GetException(RequestRejectionReasons reason)
{
BadHttpRequestException ex;
switch (reason)
{
case RequestRejectionReasons.MissingMethod:
ex = new BadHttpRequestException("Missing method.");
break;
case RequestRejectionReasons.InvalidMethod:
ex = new BadHttpRequestException("Invalid method.");
break;
case RequestRejectionReasons.MissingRequestTarget:
ex = new BadHttpRequestException("Missing request target.");
break;
case RequestRejectionReasons.MissingHTTPVersion:
ex = new BadHttpRequestException("Missing HTTP version.");
break;
case RequestRejectionReasons.UnrecognizedHTTPVersion:
ex = new BadHttpRequestException("Unrecognized HTTP version.");
break;
case RequestRejectionReasons.MissingLFInRequestLine:
ex = new BadHttpRequestException("Missing LF in request line.");
break;
case RequestRejectionReasons.HeadersCorruptedInvalidHeaderSequence:
ex = new BadHttpRequestException("Headers corrupted, invalid header sequence.");
break;
case RequestRejectionReasons.HeaderLineMustNotStartWithWhitespace:
ex = new BadHttpRequestException("Header line must not start with whitespace.");
break;
case RequestRejectionReasons.NoColonCharacterFoundInHeaderLine:
ex = new BadHttpRequestException("No ':' character found in header line.");
break;
case RequestRejectionReasons.WhitespaceIsNotAllowedInHeaderName:
ex = new BadHttpRequestException("Whitespace is not allowed in header name.");
break;
case RequestRejectionReasons.HeaderLineMustEndInCRLFOnlyCRFound:
ex = new BadHttpRequestException("Header line must end in CRLF; only CR found.");
break;
case RequestRejectionReasons.HeaderValueLineFoldingNotSupported:
ex = new BadHttpRequestException("Header value line folding not supported.");
break;
case RequestRejectionReasons.MalformedRequestInvalidHeaders:
ex = new BadHttpRequestException("Malformed request: invalid headers.");
break;
case RequestRejectionReasons.UnexpectedEndOfRequestContent:
ex = new BadHttpRequestException("Unexpected end of request content");
break;
case RequestRejectionReasons.BadChunkSuffix:
ex = new BadHttpRequestException("Bad chunk suffix");
break;
case RequestRejectionReasons.BadChunkSizeData:
ex = new BadHttpRequestException("Bad chunk size data");
break;
case RequestRejectionReasons.ChunkedRequestIncomplete:
ex = new BadHttpRequestException("Chunked request incomplete");
break;
case RequestRejectionReasons.PathContainsNullCharacters:
ex = new BadHttpRequestException("The path contains null characters.");
break;
case RequestRejectionReasons.InvalidCharactersInHeaderName:
ex = new BadHttpRequestException("Invalid characters in header name.");
break;
case RequestRejectionReasons.NullCharactersInInputString:
ex = new BadHttpRequestException("The input string contains non-ASCII or null characters.");
break;
default:
ex = new BadHttpRequestException("Bad request.");
break;
}
return ex;
}

internal static BadHttpRequestException GetException(RequestRejectionReasons reason, string value)
{
BadHttpRequestException ex;
switch (reason)
{
case RequestRejectionReasons.MalformedRequestLineStatus:
ex = new BadHttpRequestException($"Malformed request: {value}");
break;
case RequestRejectionReasons.InvalidContentLength:
ex = new BadHttpRequestException($"Invalid content length: {value}");
break;
default:
ex = new BadHttpRequestException("Bad request.");
break;
}
return ex;
}
}
}
86 changes: 55 additions & 31 deletions src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Net;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -129,12 +130,13 @@ public int StatusCode
}
set
{
if (HasResponseStarted)
if (!HasResponseStarted)
{
throw new InvalidOperationException("Status code cannot be set, response has already started.");
_statusCode = value;
return;
}

_statusCode = value;
ThrowResponseAlreadyStartedException(nameof(StatusCode));
}
}

Expand All @@ -147,12 +149,13 @@ public string ReasonPhrase
}
set
{
if (HasResponseStarted)
if (!HasResponseStarted)
{
throw new InvalidOperationException("Reason phrase cannot be set, response had already started.");
_reasonPhrase = value;
return;
}

_reasonPhrase = value;
ThrowResponseAlreadyStartedException(nameof(ReasonPhrase));
}
}

Expand Down Expand Up @@ -567,30 +570,28 @@ public Task ProduceStartAndFireOnStarting()
return ProduceStartAndFireOnStartingAwaited();
}

if (_applicationException != null)
if (_applicationException == null)
{
throw new ObjectDisposedException(
"The response has been aborted due to an unhandled application exception.",
_applicationException);
ProduceStart(appCompleted: false);
return TaskUtilities.CompletedTask;
}

ProduceStart(appCompleted: false);

ThrowResponseAbortedException();
// Line can never be reached but compiler doesn't know
return TaskUtilities.CompletedTask;
}

private async Task ProduceStartAndFireOnStartingAwaited()
{
await FireOnStarting();

if (_applicationException != null)
if (_applicationException == null)
{
throw new ObjectDisposedException(
"The response has been aborted due to an unhandled application exception.",
_applicationException);
ProduceStart(appCompleted: false);
return;
}

ProduceStart(appCompleted: false);
ThrowResponseAbortedException();
}

private void ProduceStart(bool appCompleted)
Expand Down Expand Up @@ -819,7 +820,7 @@ protected RequestLineStatus TakeStartLine(SocketInput input)

if (method == null)
{
RejectRequest("Missing method.");
RejectRequest(RequestRejectionReasons.MissingMethod);
}

// Note: We're not in the fast path any more (GetKnownMethod should have handled any HTTP Method we're aware of)
Expand All @@ -828,7 +829,7 @@ protected RequestLineStatus TakeStartLine(SocketInput input)
{
if (!IsValidTokenChar(method[i]))
{
RejectRequest("Invalid method.");
RejectRequest(RequestRejectionReasons.InvalidMethod);
}
}
}
Expand Down Expand Up @@ -873,7 +874,7 @@ protected RequestLineStatus TakeStartLine(SocketInput input)

if (pathBegin.Peek() == ' ')
{
RejectRequest("Missing request target.");
RejectRequest(RequestRejectionReasons.MissingRequestTarget);
}

scan.Take();
Expand All @@ -895,11 +896,11 @@ protected RequestLineStatus TakeStartLine(SocketInput input)

if (httpVersion == null)
{
RejectRequest("Missing HTTP version.");
RejectRequest(RequestRejectionReasons.MissingHTTPVersion);
}
else if (httpVersion != "HTTP/1.0" && httpVersion != "HTTP/1.1")
{
RejectRequest("Unrecognized HTTP version.");
RejectRequest(RequestRejectionReasons.UnrecognizedHTTPVersion);
}
}

Expand All @@ -911,7 +912,7 @@ protected RequestLineStatus TakeStartLine(SocketInput input)
}
else if (next != '\n')
{
RejectRequest("Missing LF in request line.");
RejectRequest(RequestRejectionReasons.MissingLFInRequestLine);
}

// URIs are always encoded/escaped to ASCII https://tools.ietf.org/html/rfc3986#page-11
Expand Down Expand Up @@ -1065,11 +1066,11 @@ public bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHea
}

// Headers don't end in CRLF line.
RejectRequest("Headers corrupted, invalid header sequence.");
RejectRequest(RequestRejectionReasons.HeadersCorruptedInvalidHeaderSequence);
}
else if (ch == ' ' || ch == '\t')
{
RejectRequest("Header line must not start with whitespace.");
RejectRequest(RequestRejectionReasons.HeaderLineMustNotStartWithWhitespace);
}

var beginName = scan;
Expand All @@ -1082,13 +1083,13 @@ public bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHea
ch = scan.Take();
if (ch != ':')
{
RejectRequest("No ':' character found in header line.");
RejectRequest(RequestRejectionReasons.NoColonCharacterFoundInHeaderLine);
}

var validateName = beginName;
if (validateName.Seek(ref _vectorSpaces, ref _vectorTabs, ref _vectorColons) != ':')
{
RejectRequest("Whitespace is not allowed in header name.");
RejectRequest(RequestRejectionReasons.WhitespaceIsNotAllowedInHeaderName);
}

var beginValue = scan;
Expand Down Expand Up @@ -1128,7 +1129,7 @@ public bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHea
}
else if (ch != '\n')
{
RejectRequest("Header line must end in CRLF; only CR found.");
RejectRequest(RequestRejectionReasons.HeaderLineMustEndInCRLFOnlyCRFound);
}

var next = scan.Peek();
Expand All @@ -1155,7 +1156,7 @@ public bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHea
// explaining that obsolete line folding is unacceptable, or replace
// each received obs-fold with one or more SP octets prior to
// interpreting the field value or forwarding the message downstream.
RejectRequest("Header value line folding not supported.");
RejectRequest(RequestRejectionReasons.HeaderValueLineFoldingNotSupported);
}

// Trim trailing whitespace from header value by repeatedly advancing to next
Expand Down Expand Up @@ -1203,9 +1204,32 @@ public bool StatusCanHaveBody(int statusCode)
statusCode != 304;
}

public void RejectRequest(string message)
[MethodImpl(MethodImplOptions.NoInlining)]
private void ThrowResponseAlreadyStartedException(string value)
{
throw new InvalidOperationException(value + " cannot be set, response had already started.");
}

[MethodImpl(MethodImplOptions.NoInlining)]
private void ThrowResponseAbortedException()
{
throw new ObjectDisposedException(
"The response has been aborted due to an unhandled application exception.",
_applicationException);
}

[MethodImpl(MethodImplOptions.NoInlining)]
public void RejectRequest(RequestRejectionReasons reason)
{
var ex = BadHttpRequestException.GetException(reason);
SetBadRequestState(ex);
throw ex;
}

[MethodImpl(MethodImplOptions.NoInlining)]
public void RejectRequest(RequestRejectionReasons reason, string value)
{
var ex = new BadHttpRequestException(message);
var ex = BadHttpRequestException.GetException(reason, value);
SetBadRequestState(ex);
throw ex;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4903,7 +4903,7 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string
{
if (!AsciiUtilities.TryGetAsciiString(ptr, keyBuffer, keyLength))
{
throw new BadHttpRequestException("Invalid characters in header name");
throw BadHttpRequestException.GetException(RequestRejectionReasons.InvalidCharactersInHeaderName);
}
}
}
Expand Down
Loading

0 comments on commit 6b161f5

Please sign in to comment.