|
|
@@ -1147,7 +1147,21 @@ internal abstract partial class HttpProtocol : IHttpResponseControl
|
|
|
|
|
|
if (!_canWriteResponseBody && hasTransferEncoding)
|
|
|
{
|
|
|
- RejectNonBodyTransferEncodingResponse(appCompleted);
|
|
|
+ RejectInvalidHeaderForNonBodyResponse(appCompleted, HeaderNames.TransferEncoding);
|
|
|
+ }
|
|
|
+ else if (responseHeaders.ContentLength.HasValue)
|
|
|
+ {
|
|
|
+ if (!CanIncludeResponseContentLengthHeader())
|
|
|
+ {
|
|
|
+ RejectInvalidHeaderForNonBodyResponse(appCompleted, HeaderNames.ContentLength);
|
|
|
+ }
|
|
|
+ else if (StatusCode == StatusCodes.Status205ResetContent && responseHeaders.ContentLength.Value != 0)
|
|
|
+ {
|
|
|
+ // It is valid for a 205 response to have a Content-Length but it must be 0
|
|
|
+ // since 205 implies that no additional content will be provided.
|
|
|
+ // https://httpwg.org/specs/rfc7231.html#rfc.section.6.3.6
|
|
|
+ RejectNonzeroContentLengthOn205Response(appCompleted);
|
|
|
+ }
|
|
|
}
|
|
|
else if (StatusCode == StatusCodes.Status101SwitchingProtocols)
|
|
|
{
|
|
|
@@ -1157,7 +1171,6 @@ internal abstract partial class HttpProtocol : IHttpResponseControl
|
|
|
{
|
|
|
if ((appCompleted || !_canWriteResponseBody) && !_hasAdvanced) // Avoid setting contentLength of 0 if we wrote data before calling CreateResponseHeaders
|
|
|
{
|
|
|
- // Don't set the Content-Length header automatically for HEAD requests, 204 responses, or 304 responses.
|
|
|
if (CanAutoSetContentLengthZeroResponseHeader())
|
|
|
{
|
|
|
// Since the app has completed writing or cannot write to the response, we can safely set the Content-Length to 0.
|
|
|
@@ -1219,6 +1232,28 @@ internal abstract partial class HttpProtocol : IHttpResponseControl
|
|
|
return responseHeaders;
|
|
|
}
|
|
|
|
|
|
+ private bool CanIncludeResponseContentLengthHeader()
|
|
|
+ {
|
|
|
+ // Section 4.3.6 of RFC7231
|
|
|
+ if (Is1xxCode(StatusCode) || StatusCode == StatusCodes.Status204NoContent)
|
|
|
+ {
|
|
|
+ // A server MUST NOT send a Content-Length header field in any response
|
|
|
+ // with a status code of 1xx (Informational) or 204 (No Content).
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ else if (Method == HttpMethod.Connect && Is2xxCode(StatusCode))
|
|
|
+ {
|
|
|
+ // A server MUST NOT send a Content-Length header field in any 2xx
|
|
|
+ // (Successful) response to a CONNECT request.
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+
|
|
|
+ static bool Is1xxCode(int code) => code >= StatusCodes.Status100Continue && code < StatusCodes.Status200OK;
|
|
|
+ static bool Is2xxCode(int code) => code >= StatusCodes.Status200OK && code < StatusCodes.Status300MultipleChoices;
|
|
|
+ }
|
|
|
+
|
|
|
private bool CanWriteResponseBody()
|
|
|
{
|
|
|
// List of status codes taken from Microsoft.Net.Http.Server.Response
|
|
|
@@ -1230,9 +1265,12 @@ internal abstract partial class HttpProtocol : IHttpResponseControl
|
|
|
|
|
|
private bool CanAutoSetContentLengthZeroResponseHeader()
|
|
|
{
|
|
|
- return Method != HttpMethod.Head &&
|
|
|
- StatusCode != StatusCodes.Status204NoContent &&
|
|
|
- StatusCode != StatusCodes.Status304NotModified;
|
|
|
+ return CanIncludeResponseContentLengthHeader() &&
|
|
|
+ // Responses to HEAD may omit Content-Length (Section 4.3.6 of RFC7231).
|
|
|
+ Method != HttpMethod.Head &&
|
|
|
+ // 304s should only include specific fields, of which Content-Length is
|
|
|
+ // not one (Section 4.1 of RFC7232).
|
|
|
+ StatusCode != StatusCodes.Status304NotModified;
|
|
|
}
|
|
|
|
|
|
private static void ThrowResponseAlreadyStartedException(string value)
|
|
|
@@ -1240,9 +1278,15 @@ internal abstract partial class HttpProtocol : IHttpResponseControl
|
|
|
throw new InvalidOperationException(CoreStrings.FormatParameterReadOnlyAfterResponseStarted(value));
|
|
|
}
|
|
|
|
|
|
- private void RejectNonBodyTransferEncodingResponse(bool appCompleted)
|
|
|
+ private void RejectInvalidHeaderForNonBodyResponse(bool appCompleted, string headerName)
|
|
|
+ => RejectInvalidResponse(appCompleted, CoreStrings.FormatHeaderNotAllowedOnResponse(headerName, StatusCode));
|
|
|
+
|
|
|
+ private void RejectNonzeroContentLengthOn205Response(bool appCompleted)
|
|
|
+ => RejectInvalidResponse(appCompleted, CoreStrings.NonzeroContentLengthNotAllowedOn205);
|
|
|
+
|
|
|
+ private void RejectInvalidResponse(bool appCompleted, string message)
|
|
|
{
|
|
|
- var ex = new InvalidOperationException(CoreStrings.FormatHeaderNotAllowedOnResponse("Transfer-Encoding", StatusCode));
|
|
|
+ var ex = new InvalidOperationException(message);
|
|
|
if (!appCompleted)
|
|
|
{
|
|
|
// Back out of header creation surface exception in user code
|