Permalink
Browse files

Merge branch 'benaadams/exceptions' into dev

  • Loading branch information...
2 parents 3a67cb9 + 51288e1 commit 2244f190e16ab30d1a216ba0d2f7488aa0ab1262 @cesarbs cesarbs committed Jul 25, 2016
View
93 src/Microsoft.AspNetCore.Server.Kestrel/BadHttpRequestException.cs
@@ -2,15 +2,106 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
+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)
{
}
+
+ internal static BadHttpRequestException GetException(RequestRejectionReason reason)
+ {
+ BadHttpRequestException ex;
+ switch (reason)
+ {
+ case RequestRejectionReason.MissingMethod:
+ ex = new BadHttpRequestException("Missing method.");
+ break;
+ case RequestRejectionReason.InvalidMethod:
+ ex = new BadHttpRequestException("Invalid method.");
+ break;
+ case RequestRejectionReason.MissingRequestTarget:
+ ex = new BadHttpRequestException("Missing request target.");
+ break;
+ case RequestRejectionReason.MissingHTTPVersion:
+ ex = new BadHttpRequestException("Missing HTTP version.");
+ break;
+ case RequestRejectionReason.UnrecognizedHTTPVersion:
+ ex = new BadHttpRequestException("Unrecognized HTTP version.");
+ break;
+ case RequestRejectionReason.MissingLFInRequestLine:
+ ex = new BadHttpRequestException("Missing LF in request line.");
+ break;
+ case RequestRejectionReason.HeadersCorruptedInvalidHeaderSequence:
+ ex = new BadHttpRequestException("Headers corrupted, invalid header sequence.");
+ break;
+ case RequestRejectionReason.HeaderLineMustNotStartWithWhitespace:
+ ex = new BadHttpRequestException("Header line must not start with whitespace.");
+ break;
+ case RequestRejectionReason.NoColonCharacterFoundInHeaderLine:
+ ex = new BadHttpRequestException("No ':' character found in header line.");
+ break;
+ case RequestRejectionReason.WhitespaceIsNotAllowedInHeaderName:
+ ex = new BadHttpRequestException("Whitespace is not allowed in header name.");
+ break;
+ case RequestRejectionReason.HeaderLineMustEndInCRLFOnlyCRFound:
+ ex = new BadHttpRequestException("Header line must end in CRLF; only CR found.");
+ break;
+ case RequestRejectionReason.HeaderValueLineFoldingNotSupported:
+ ex = new BadHttpRequestException("Header value line folding not supported.");
+ break;
+ case RequestRejectionReason.MalformedRequestInvalidHeaders:
+ ex = new BadHttpRequestException("Malformed request: invalid headers.");
+ break;
+ case RequestRejectionReason.UnexpectedEndOfRequestContent:
+ ex = new BadHttpRequestException("Unexpected end of request content.");
+ break;
+ case RequestRejectionReason.BadChunkSuffix:
+ ex = new BadHttpRequestException("Bad chunk suffix.");
+ break;
+ case RequestRejectionReason.BadChunkSizeData:
+ ex = new BadHttpRequestException("Bad chunk size data.");
+ break;
+ case RequestRejectionReason.ChunkedRequestIncomplete:
+ ex = new BadHttpRequestException("Chunked request incomplete.");
+ break;
+ case RequestRejectionReason.PathContainsNullCharacters:
+ ex = new BadHttpRequestException("The path contains null characters.");
+ break;
+ case RequestRejectionReason.InvalidCharactersInHeaderName:
+ ex = new BadHttpRequestException("Invalid characters in header name.");
+ break;
+ case RequestRejectionReason.NonAsciiOrNullCharactersInInputString:
+ 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(RequestRejectionReason reason, string value)
+ {
+ BadHttpRequestException ex;
+ switch (reason)
+ {
+ case RequestRejectionReason.MalformedRequestLineStatus:
+ ex = new BadHttpRequestException($"Malformed request: {value}");
+ break;
+ case RequestRejectionReason.InvalidContentLength:
+ ex = new BadHttpRequestException($"Invalid content length: {value}");
+ break;
+ default:
+ ex = new BadHttpRequestException("Bad request.");
+ break;
+ }
+ return ex;
+ }
}
}
View
60 src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Frame.cs
@@ -131,7 +131,7 @@ public int StatusCode
{
if (HasResponseStarted)
{
- throw new InvalidOperationException("Status code cannot be set, response has already started.");
+ ThrowResponseAlreadyStartedException(nameof(StatusCode));
}
_statusCode = value;
@@ -149,7 +149,7 @@ public string ReasonPhrase
{
if (HasResponseStarted)
{
- throw new InvalidOperationException("Reason phrase cannot be set, response had already started.");
+ ThrowResponseAlreadyStartedException(nameof(ReasonPhrase));
}
_reasonPhrase = value;
@@ -569,13 +569,10 @@ public Task ProduceStartAndFireOnStarting()
if (_applicationException != null)
{
- throw new ObjectDisposedException(
- "The response has been aborted due to an unhandled application exception.",
- _applicationException);
+ ThrowResponseAbortedException();
}
ProduceStart(appCompleted: false);
-
return TaskUtilities.CompletedTask;
}
@@ -585,9 +582,7 @@ public Task ProduceStartAndFireOnStarting()
if (_applicationException != null)
{
- throw new ObjectDisposedException(
- "The response has been aborted due to an unhandled application exception.",
- _applicationException);
+ ThrowResponseAbortedException();
}
ProduceStart(appCompleted: false);
@@ -819,7 +814,7 @@ protected RequestLineStatus TakeStartLine(SocketInput input)
if (method == null)
{
- RejectRequest("Missing method.");
+ RejectRequest(RequestRejectionReason.MissingMethod);
}
// Note: We're not in the fast path any more (GetKnownMethod should have handled any HTTP Method we're aware of)
@@ -828,7 +823,7 @@ protected RequestLineStatus TakeStartLine(SocketInput input)
{
if (!IsValidTokenChar(method[i]))
{
- RejectRequest("Invalid method.");
+ RejectRequest(RequestRejectionReason.InvalidMethod);
}
}
}
@@ -873,7 +868,7 @@ protected RequestLineStatus TakeStartLine(SocketInput input)
if (pathBegin.Peek() == ' ')
{
- RejectRequest("Missing request target.");
+ RejectRequest(RequestRejectionReason.MissingRequestTarget);
}
scan.Take();
@@ -895,11 +890,11 @@ protected RequestLineStatus TakeStartLine(SocketInput input)
if (httpVersion == null)
{
- RejectRequest("Missing HTTP version.");
+ RejectRequest(RequestRejectionReason.MissingHTTPVersion);
}
else if (httpVersion != "HTTP/1.0" && httpVersion != "HTTP/1.1")
{
- RejectRequest("Unrecognized HTTP version.");
+ RejectRequest(RequestRejectionReason.UnrecognizedHTTPVersion);
}
}
@@ -911,7 +906,7 @@ protected RequestLineStatus TakeStartLine(SocketInput input)
}
else if (next != '\n')
{
- RejectRequest("Missing LF in request line.");
+ RejectRequest(RequestRejectionReason.MissingLFInRequestLine);
}
// URIs are always encoded/escaped to ASCII https://tools.ietf.org/html/rfc3986#page-11
@@ -1065,11 +1060,11 @@ public bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHea
}
// Headers don't end in CRLF line.
- RejectRequest("Headers corrupted, invalid header sequence.");
+ RejectRequest(RequestRejectionReason.HeadersCorruptedInvalidHeaderSequence);
}
else if (ch == ' ' || ch == '\t')
{
- RejectRequest("Header line must not start with whitespace.");
+ RejectRequest(RequestRejectionReason.HeaderLineMustNotStartWithWhitespace);
}
var beginName = scan;
@@ -1082,13 +1077,13 @@ public bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHea
ch = scan.Take();
if (ch != ':')
{
- RejectRequest("No ':' character found in header line.");
+ RejectRequest(RequestRejectionReason.NoColonCharacterFoundInHeaderLine);
}
var validateName = beginName;
if (validateName.Seek(ref _vectorSpaces, ref _vectorTabs, ref _vectorColons) != ':')
{
- RejectRequest("Whitespace is not allowed in header name.");
+ RejectRequest(RequestRejectionReason.WhitespaceIsNotAllowedInHeaderName);
}
var beginValue = scan;
@@ -1128,7 +1123,7 @@ public bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHea
}
else if (ch != '\n')
{
- RejectRequest("Header line must end in CRLF; only CR found.");
+ RejectRequest(RequestRejectionReason.HeaderLineMustEndInCRLFOnlyCRFound);
}
var next = scan.Peek();
@@ -1155,7 +1150,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(RequestRejectionReason.HeaderValueLineFoldingNotSupported);
}
// Trim trailing whitespace from header value by repeatedly advancing to next
@@ -1203,9 +1198,28 @@ public bool StatusCanHaveBody(int statusCode)
statusCode != 304;
}
- public void RejectRequest(string message)
+ private void ThrowResponseAlreadyStartedException(string value)
+ {
+ throw new InvalidOperationException(value + " cannot be set, response had already started.");
+ }
+
+ private void ThrowResponseAbortedException()
+ {
+ throw new ObjectDisposedException(
+ "The response has been aborted due to an unhandled application exception.",
+ _applicationException);
+ }
+
+ public void RejectRequest(RequestRejectionReason reason)
+ {
+ var ex = BadHttpRequestException.GetException(reason);
+ SetBadRequestState(ex);
+ throw ex;
+ }
+
+ public void RejectRequest(RequestRejectionReason reason, string value)
{
- var ex = new BadHttpRequestException(message);
+ var ex = BadHttpRequestException.GetException(reason, value);
SetBadRequestState(ex);
throw ex;
}
View
2 src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/FrameHeaders.Generated.cs
@@ -4904,7 +4904,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(RequestRejectionReason.InvalidCharactersInHeaderName);
}
}
}
View
17 src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/FrameHeaders.cs
@@ -29,7 +29,7 @@ public abstract class FrameHeaders : IHeaderDictionary
{
if (_isReadOnly)
{
- ThrowReadOnlyException();
+ ThrowHeadersReadOnlyException();
}
SetValueFast(key, value);
}
@@ -48,7 +48,7 @@ public abstract class FrameHeaders : IHeaderDictionary
}
}
- protected void ThrowReadOnlyException()
+ protected void ThrowHeadersReadOnlyException()
{
throw new InvalidOperationException("Headers are read-only, response has already started.");
}
@@ -144,7 +144,7 @@ protected virtual void CopyToFast(KeyValuePair<string, StringValues>[] array, in
{
if (_isReadOnly)
{
- ThrowReadOnlyException();
+ ThrowHeadersReadOnlyException();
}
AddValueFast(key, value);
}
@@ -153,7 +153,7 @@ protected virtual void CopyToFast(KeyValuePair<string, StringValues>[] array, in
{
if (_isReadOnly)
{
- ThrowReadOnlyException();
+ ThrowHeadersReadOnlyException();
}
ClearFast();
}
@@ -200,7 +200,7 @@ IEnumerator IEnumerable.GetEnumerator()
{
if (_isReadOnly)
{
- ThrowReadOnlyException();
+ ThrowHeadersReadOnlyException();
}
return RemoveFast(key);
}
@@ -226,10 +226,15 @@ public static void ValidateHeaderCharacters(string headerCharacters)
{
if (ch < 0x20 || ch > 0x7E)
{
- throw new InvalidOperationException(string.Format("Invalid non-ASCII or control character in header: 0x{0:X4}", (ushort)ch));
+ ThrowInvalidHeaderCharacter(ch);
}
}
}
}
+
+ private static void ThrowInvalidHeaderCharacter(char ch)
+ {
+ throw new InvalidOperationException(string.Format("Invalid non-ASCII or control character in header: 0x{0:X4}", (ushort)ch));
+ }
}
}
View
4 src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/FrameOfT.cs
@@ -49,7 +49,7 @@ public class Frame<TContext> : Frame
if (requestLineStatus != RequestLineStatus.Done)
{
- RejectRequest($"Malformed request: {requestLineStatus}");
+ RejectRequest(RequestRejectionReason.MalformedRequestLineStatus, requestLineStatus.ToString());
}
break;
@@ -70,7 +70,7 @@ public class Frame<TContext> : Frame
// sent immediately before the a FIN from the client.
if (!TakeMessageHeaders(SocketInput, FrameRequestHeaders))
{
- RejectRequest($"Malformed request: invalid headers.");
+ RejectRequest(RequestRejectionReason.MalformedRequestInvalidHeaders);
}
break;
View
27 src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/MessageBody.cs
@@ -122,7 +122,7 @@ public Task Consume(CancellationToken cancellationToken = default(CancellationTo
long contentLength;
if (!long.TryParse(unparsedContentLength, out contentLength) || contentLength < 0)
{
- context.RejectRequest($"Invalid content length: {unparsedContentLength}");
+ context.RejectRequest(RequestRejectionReason.InvalidContentLength, unparsedContentLength);
}
else
{
@@ -184,7 +184,7 @@ public override ValueTask<int> ReadAsyncImplementation(ArraySegment<byte> buffer
_inputLength -= actual;
if (actual == 0)
{
- _context.RejectRequest("Unexpected end of request content");
+ _context.RejectRequest(RequestRejectionReason.UnexpectedEndOfRequestContent);
}
return new ValueTask<int>(actual);
}
@@ -200,7 +200,7 @@ public override ValueTask<int> ReadAsyncImplementation(ArraySegment<byte> buffer
_inputLength -= actual;
if (actual == 0)
{
- _context.RejectRequest("Unexpected end of request content");
+ _context.RejectRequest(RequestRejectionReason.UnexpectedEndOfRequestContent);
}
return actual;
@@ -248,7 +248,7 @@ public override ValueTask<int> ReadAsyncImplementation(ArraySegment<byte> buffer
}
else if (fin)
{
- ThrowChunkedRequestIncomplete();
+ _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
}
await input;
@@ -266,7 +266,7 @@ public override ValueTask<int> ReadAsyncImplementation(ArraySegment<byte> buffer
}
else if (fin)
{
- ThrowChunkedRequestIncomplete();
+ _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
}
await input;
@@ -288,7 +288,7 @@ public override ValueTask<int> ReadAsyncImplementation(ArraySegment<byte> buffer
}
else if (fin)
{
- ThrowChunkedRequestIncomplete();
+ _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
}
await input;
@@ -306,7 +306,7 @@ public override ValueTask<int> ReadAsyncImplementation(ArraySegment<byte> buffer
}
else if (fin)
{
- ThrowChunkedRequestIncomplete();
+ _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
}
await input;
@@ -326,7 +326,7 @@ public override ValueTask<int> ReadAsyncImplementation(ArraySegment<byte> buffer
}
else if (fin)
{
- ThrowChunkedRequestIncomplete();
+ _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
}
await input;
@@ -344,7 +344,7 @@ public override ValueTask<int> ReadAsyncImplementation(ArraySegment<byte> buffer
}
else
{
- ThrowChunkedRequestIncomplete();
+ _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
}
}
@@ -503,7 +503,7 @@ private void ParseChunkedSuffix(SocketInput input)
}
else
{
- _context.RejectRequest("Bad chunk suffix");
+ _context.RejectRequest(RequestRejectionReason.BadChunkSuffix);
}
}
finally
@@ -559,15 +559,10 @@ private int CalculateChunkSize(int extraHexDigit, int currentParsedSize)
}
}
- _context.RejectRequest("Bad chunk size data");
+ _context.RejectRequest(RequestRejectionReason.BadChunkSizeData);
return -1; // can't happen, but compiler complains
}
- private void ThrowChunkedRequestIncomplete()
- {
- _context.RejectRequest("Chunked request incomplete");
- }
-
private enum Mode
{
Prefix,
View
31 src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/RequestRejectionReason.cs
@@ -0,0 +1,31 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
+{
+ public enum RequestRejectionReason
+ {
+ MissingMethod,
+ InvalidMethod,
+ MissingRequestTarget,
+ MissingHTTPVersion,
+ UnrecognizedHTTPVersion,
+ MissingLFInRequestLine,
+ HeadersCorruptedInvalidHeaderSequence,
+ HeaderLineMustNotStartWithWhitespace,
+ NoColonCharacterFoundInHeaderLine,
+ WhitespaceIsNotAllowedInHeaderName,
+ HeaderLineMustEndInCRLFOnlyCRFound,
+ HeaderValueLineFoldingNotSupported,
+ MalformedRequestLineStatus,
+ MalformedRequestInvalidHeaders,
+ InvalidContentLength,
+ UnexpectedEndOfRequestContent,
+ BadChunkSuffix,
+ BadChunkSizeData,
+ ChunkedRequestIncomplete,
+ PathContainsNullCharacters,
+ InvalidCharactersInHeaderName,
+ NonAsciiOrNullCharactersInInputString
+ }
+}
View
2 src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/UrlPathDecoder.cs
@@ -67,7 +67,7 @@ private static bool DecodeCore(ref MemoryPoolIterator reader, ref MemoryPoolIter
if (byte1 == 0)
{
- throw new BadHttpRequestException("The path contains null characters.");
+ throw BadHttpRequestException.GetException(RequestRejectionReason.PathContainsNullCharacters);
}
if (byte1 == -1)
View
3 ...crosoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/MemoryPoolIteratorExtensions.cs
@@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using System.Text;
+using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
{
@@ -115,7 +116,7 @@ public unsafe static string GetAsciiString(this MemoryPoolIterator start, Memory
{
if (!AsciiUtilities.TryGetAsciiString(block.DataFixedPtr + inputOffset, output + outputOffset, following))
{
- throw new BadHttpRequestException("The input string contains non-ASCII or null characters.");
+ throw BadHttpRequestException.GetException(RequestRejectionReason.NonAsciiOrNullCharactersInInputString);
}
outputOffset += following;
View
2 tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/KnownHeaders.cs
@@ -451,7 +451,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(RequestRejectionReason.InvalidCharactersInHeaderName);
}}
}}
}}

0 comments on commit 2244f19

Please sign in to comment.