Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public DateTimeOffset? Date
return true;
}

if (parent.ContainsParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked))
if (parent.ContainsParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked, lastValueOnly:true))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (parent.ContainsParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked, lastValueOnly:true))
if (parent.ContainsParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked, lastValueOnly: true))

{
return true;
}
Expand Down Expand Up @@ -117,8 +117,14 @@ public bool? TransferEncodingChunked
if (value == true)
{
_transferEncodingChunkedSet = true;
if (!_parent.ContainsParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked))
if (!_parent.ContainsParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked, lastValueOnly: true))
{
if (_parent.ContainsParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked, lastValueOnly: false))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will now do an extra pass over the values in the common case where the value wasn't set yet.

Can we instead do something like

_parent.RemoveParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked);
_parent.AddParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked);

{
// According to https://www.rfc-editor.org/rfc/rfc7230.html#section-3.3.1 "chunked" must be the last value in the Transfer-Encoding header.
// We have to remove it and add it as last.
_parent.RemoveParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked);
}
_parent.AddParsedValue(KnownHeaders.TransferEncoding.Descriptor, HeaderUtilities.TransferEncodingChunked);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ internal bool RemoveParsedValue(HeaderDescriptor descriptor, object value)
return false;
}

internal bool ContainsParsedValue(HeaderDescriptor descriptor, object value)
internal bool ContainsParsedValue(HeaderDescriptor descriptor, object value, bool lastValueOnly = false)
{
Debug.Assert(value != null);

Expand All @@ -516,6 +516,13 @@ internal bool ContainsParsedValue(HeaderDescriptor descriptor, object value)
}

List<object>? parsedValues = parsedValue as List<object>;
if (lastValueOnly && parsedValues?.Count > 1)
{
// This can be useful for specific header value pair like for example "TransferEncoding: gzip, chunked" where chunked must be last.
// Relevant RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.3.1
parsedValue = parsedValues[^1];
parsedValues = null;
}

IEqualityComparer? comparer = descriptor.Parser.Comparer;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2638,6 +2638,53 @@ public void TryAddWithoutValidation_ThreeValidValueHeader_UseGenericImplementati
Assert.True(expectedValues.SequenceEqual(values));
}

[Theory]
[InlineData(new object[] { "chunked" }, true)]
[InlineData(new object[] { "gzip", "chunked" }, true)]
[InlineData(new object[] { "gzip", "chunked", "deflate", "chunked" }, true)]
[InlineData(new object[] { true }, true)]
[InlineData(new object[] { true, null, true }, true)]
[InlineData(new object[] { "gzip", true }, true)]
[InlineData(new object[] { "gzip", true, "deflate", true }, true)]
[InlineData(new object[] { true, "deflate", true }, true)]
[InlineData(new object[] { false, "deflate", true }, true)]
[InlineData(new object[] { false }, false)]
[InlineData(new object[] { false, null, false }, false)]
[InlineData(new object[] { "gzip", false }, false)]
[InlineData(new object[] { false, "gzip" }, false)]
[InlineData(new object[] { "gzip", true, false }, false)]
[InlineData(new object[] { "gzip", true, "deflate" }, false)]
[InlineData(new object[] { "gzip", true, "deflate", false }, false)]
[InlineData(new object[] { }, null)]
[InlineData(new object[] { null }, null)]
[InlineData(new object[] { "gzip" }, null)]
[InlineData(new object[] { "chunked", "gzip" }, null)]
[InlineData(new object[] { "gzip", "deflate" }, null)]
[InlineData(new object[] { "gzip", "chunked", "gzip" }, null)]
[InlineData(new object[] { "gzip", true, null}, null)]
public void AddTransferEncoding_SetValues_ChunkedAsExpected(object?[] values, bool? expected)
{
var req = new HttpRequestHeaders();
var resp = new HttpResponseHeaders();

foreach (object value in values)
{
if (value is bool? || value is null)
{
req.TransferEncodingChunked = (bool?)value;
resp.TransferEncodingChunked = (bool?)value;
}
else
{
req.TransferEncoding.ParseAdd((string)value);
resp.TransferEncoding.ParseAdd((string)value);
}
}

Assert.Equal(expected, req.TransferEncodingChunked);
Assert.Equal(expected, resp.TransferEncodingChunked);
}

public static IEnumerable<object[]> NumberOfHeadersUpToArrayThreshold_AddNonValidated_EnumerateNonValidated()
{
for (int i = 0; i <= HttpHeaders.ArrayThreshold; i++)
Expand Down