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

Commit c627c44

Browse files
committed
Don't emit TE header or body for non-body responses
Resolves #952
1 parent 82a0a99 commit c627c44

File tree

2 files changed

+45
-23
lines changed

2 files changed

+45
-23
lines changed

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Frame.cs

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public abstract partial class Frame : ConnectionContext, IFrameControl
5858

5959
protected RequestProcessingStatus _requestProcessingStatus;
6060
protected bool _keepAlive;
61+
private bool _canHaveBody;
6162
private bool _autoChunk;
6263
protected Exception _applicationException;
6364

@@ -465,6 +466,9 @@ public void Write(ArraySegment<byte> data)
465466
{
466467
ProduceStartAndFireOnStarting().GetAwaiter().GetResult();
467468

469+
// Don't write to body for HEAD requests or 101, 204, 205, 304 responses.
470+
if (!_canHaveBody) return;
471+
468472
if (_autoChunk)
469473
{
470474
if (data.Count == 0)
@@ -486,6 +490,9 @@ public Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationTo
486490
return WriteAsyncAwaited(data, cancellationToken);
487491
}
488492

493+
// Don't write to body for HEAD requests or 101, 204, 205, 304 responses.
494+
if (!_canHaveBody) return TaskUtilities.CompletedTask;
495+
489496
if (_autoChunk)
490497
{
491498
if (data.Count == 0)
@@ -504,6 +511,9 @@ public async Task WriteAsyncAwaited(ArraySegment<byte> data, CancellationToken c
504511
{
505512
await ProduceStartAndFireOnStarting();
506513

514+
// Don't write to body for HEAD requests or 101, 204, 205, 304 responses.
515+
if (!_canHaveBody) return;
516+
507517
if (_autoChunk)
508518
{
509519
if (data.Count == 0)
@@ -710,10 +720,12 @@ private void CreateResponseHeader(
710720
bool appCompleted)
711721
{
712722
var responseHeaders = FrameResponseHeaders;
713-
responseHeaders.SetReadOnly();
714723

715724
var hasConnection = responseHeaders.HasConnection;
716725

726+
// Set whether response can have body
727+
_canHaveBody = StatusCanHaveBody(StatusCode) && Method != "HEAD";
728+
717729
var end = SocketOutput.ProducingStart();
718730
if (_keepAlive && hasConnection)
719731
{
@@ -727,39 +739,49 @@ private void CreateResponseHeader(
727739
}
728740
}
729741

730-
if (_keepAlive && !responseHeaders.HasTransferEncoding && !responseHeaders.HasContentLength)
742+
if (_canHaveBody)
731743
{
732-
if (appCompleted)
744+
if (_keepAlive && !responseHeaders.HasTransferEncoding && !responseHeaders.HasContentLength)
733745
{
734-
// Don't set the Content-Length or Transfer-Encoding headers
735-
// automatically for HEAD requests or 101, 204, 205, 304 responses.
736-
if (Method != "HEAD" && StatusCanHaveBody(StatusCode))
746+
if (appCompleted)
737747
{
738748
// Since the app has completed and we are only now generating
739749
// the headers we can safely set the Content-Length to 0.
740750
responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
741751
}
742-
}
743-
else
744-
{
745-
// Note for future reference: never change this to set _autoChunk to true on HTTP/1.0
746-
// connections, even if we were to infer the client supports it because an HTTP/1.0 request
747-
// was received that used chunked encoding. Sending a chunked response to an HTTP/1.0
748-
// client would break compliance with RFC 7230 (section 3.3.1):
749-
//
750-
// A server MUST NOT send a response containing Transfer-Encoding unless the corresponding
751-
// request indicates HTTP/1.1 (or later).
752-
if (_httpVersion == HttpVersionType.Http11)
753-
{
754-
_autoChunk = true;
755-
responseHeaders.SetRawTransferEncoding("chunked", _bytesTransferEncodingChunked);
756-
}
757752
else
758753
{
759-
_keepAlive = false;
754+
// Note for future reference: never change this to set _autoChunk to true on HTTP/1.0
755+
// connections, even if we were to infer the client supports it because an HTTP/1.0 request
756+
// was received that used chunked encoding. Sending a chunked response to an HTTP/1.0
757+
// client would break compliance with RFC 7230 (section 3.3.1):
758+
//
759+
// A server MUST NOT send a response containing Transfer-Encoding unless the corresponding
760+
// request indicates HTTP/1.1 (or later).
761+
if (_httpVersion == HttpVersionType.Http11)
762+
{
763+
_autoChunk = true;
764+
responseHeaders.SetRawTransferEncoding("chunked", _bytesTransferEncodingChunked);
765+
}
766+
else
767+
{
768+
_keepAlive = false;
769+
}
760770
}
761771
}
762772
}
773+
else
774+
{
775+
// Don't set the Content-Length or Transfer-Encoding headers
776+
// automatically for HEAD requests or 101, 204, 205, 304 responses.
777+
if (responseHeaders.HasTransferEncoding)
778+
{
779+
// Remove Transfer-Encoding encoding header if it exists
780+
responseHeaders.Remove("Transfer-Encoding");
781+
}
782+
}
783+
784+
responseHeaders.SetReadOnly();
763785

764786
if (!_keepAlive && !hasConnection && _httpVersion != HttpVersionType.Http10)
765787
{

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/FrameHeaders.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ bool ICollection<KeyValuePair<string, StringValues>>.Remove(KeyValuePair<string,
196196
RemoveFast(item.Key);
197197
}
198198

199-
bool IDictionary<string, StringValues>.Remove(string key)
199+
public bool Remove(string key)
200200
{
201201
if (_isReadOnly)
202202
{

0 commit comments

Comments
 (0)