Skip to content

Commit fddab79

Browse files
authored
Ensure HttpSys request header dictionary removes known headers when Remove is called before Get dotnet#43230 (dotnet#44860)
1 parent 37c338e commit fddab79

File tree

5 files changed

+1059
-814
lines changed

5 files changed

+1059
-814
lines changed

src/Servers/HttpSys/src/RequestProcessing/RequestHeaders.Generated.tt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,17 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
166166
<# foreach(var length in lengths) { #>
167167
case <#=length.Key#>:
168168
<# foreach(var prop in length) { #>
169-
if (_<#=prop.Name#>.Count > 0
170-
&& string.Equals(key, HeaderNames.<#=prop.Name#>, StringComparison.Ordinal))
169+
if(string.Equals(key, HeaderNames.<#=prop.Name#>, StringComparison.Ordinal))
171170
{
172-
bool wasSet = <#=IsRead(prop.Index)#>;
173-
<#=prop.Name#> = StringValues.Empty;
174-
return wasSet;
171+
if ((<#=IsRead(prop.Index)#> && <#=prop.Name#>.Count > 0)
172+
|| (!<#=IsRead(prop.Index)#> && HasKnownHeader(<#=prop.ID#>)))
173+
{
174+
<#=prop.Name#> = StringValues.Empty;
175+
return true;
176+
}
177+
return false;
175178
}
179+
176180
<# } #>
177181
break;
178182
<# } #>

src/Servers/HttpSys/test/FunctionalTests/Listener/RequestHeaderTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,46 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener;
1515

1616
public class RequestHeaderTests
1717
{
18+
[ConditionalFact]
19+
public async Task RequestHeaders_RemoveHeaders_Success()
20+
{
21+
string address;
22+
using (var server = Utilities.CreateHttpServer(out address))
23+
{
24+
string[] customValues = new string[] { "custom1, and custom测试2", "custom3" };
25+
Task responseTask = SendRequestAsync(address, "Custom-Header", customValues, Encoding.UTF8);
26+
27+
var context = await server.AcceptAsync(Utilities.DefaultTimeout);
28+
var requestHeaders = context.Request.Headers;
29+
30+
var headers = context.Request.Headers;
31+
bool removed = headers.Remove("Connection");
32+
Assert.False(headers.TryGetValue("Connection", out _));
33+
Assert.True(StringValues.IsNullOrEmpty(headers["Connection"]));
34+
Assert.True(StringValues.IsNullOrEmpty(headers.Connection));
35+
36+
removed = headers.Remove("Custom-Header");
37+
Assert.True(removed);
38+
Assert.False(headers.TryGetValue("Custom-Header", out _));
39+
Assert.True(StringValues.IsNullOrEmpty(headers["Custom-Header"]));
40+
41+
headers["Connection"] = "foo";
42+
Assert.True(headers.TryGetValue("Connection", out var connectionValue));
43+
Assert.Equal("foo", connectionValue);
44+
Assert.Equal("foo", headers["Connection"]);
45+
46+
bool removedAfterAssignValue = headers.Remove("Connection");
47+
bool removedAgain = headers.Remove("Connection");
48+
49+
Assert.True(removed);
50+
Assert.True(removedAfterAssignValue);
51+
Assert.False(removedAgain);
52+
53+
context.Dispose();
54+
55+
await responseTask;
56+
}
57+
}
1858

1959
[ConditionalFact]
2060
public async Task RequestHeaders_ClientSendsUtf8Headers_Success()

src/Shared/HttpSys/RequestProcessing/NativeRequestContext.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,38 @@ private static string GetAuthTypeFromRequest(HttpApiTypes.HTTP_REQUEST_AUTH_TYPE
307307
}
308308
}
309309

310+
internal bool HasKnownHeader(HttpSysRequestHeader header)
311+
{
312+
if (PermanentlyPinned)
313+
{
314+
return HasKnowHeaderHelper(header, 0, _nativeRequest);
315+
}
316+
else
317+
{
318+
fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span)
319+
{
320+
var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment);
321+
long fixup = pMemoryBlob - (byte*)_originalBufferAddress;
322+
return HasKnowHeaderHelper(header, fixup, request);
323+
}
324+
}
325+
}
326+
327+
private bool HasKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HttpApiTypes.HTTP_REQUEST* request)
328+
{
329+
int headerIndex = (int)header;
330+
331+
HttpApiTypes.HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + headerIndex;
332+
// For known headers, when header value is empty, RawValueLength will be 0 and
333+
// pRawValue will point to empty string ("\0")
334+
if (pKnownHeader->RawValueLength > 0)
335+
{
336+
return true;
337+
}
338+
339+
return false;
340+
}
341+
310342
// These methods are for accessing the request structure after it has been unpinned. They need to adjust addresses
311343
// in case GC has moved the original object.
312344

@@ -325,7 +357,7 @@ private static string GetAuthTypeFromRequest(HttpApiTypes.HTTP_REQUEST_AUTH_TYPE
325357
return GetKnowHeaderHelper(header, fixup, request);
326358
}
327359
}
328-
}
360+
}
329361

330362
private string? GetKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HttpApiTypes.HTTP_REQUEST* request)
331363
{

0 commit comments

Comments
 (0)