Skip to content

Commit 4383118

Browse files
authored
IIS: Identify if a request has a body (#25381)
1 parent ecdcc75 commit 4383118

File tree

6 files changed

+474
-2
lines changed

6 files changed

+474
-2
lines changed

src/Servers/IIS/IIS/src/Core/IISHttpContext.FeatureCollection.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
2424
{
2525
internal partial class IISHttpContext : IFeatureCollection,
2626
IHttpRequestFeature,
27+
IHttpRequestBodyDetectionFeature,
2728
IHttpResponseFeature,
2829
IHttpResponseBodyFeature,
2930
IHttpUpgradeFeature,
@@ -141,6 +142,8 @@ Stream IHttpRequestFeature.Body
141142
set => RequestBody = value;
142143
}
143144

145+
bool IHttpRequestBodyDetectionFeature.CanHaveBody => RequestCanHaveBody;
146+
144147
int IHttpResponseFeature.StatusCode
145148
{
146149
get => StatusCode;

src/Servers/IIS/IIS/src/Core/IISHttpContext.Features.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
99
internal partial class IISHttpContext
1010
{
1111
private static readonly Type IHttpRequestFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestFeature);
12+
private static readonly Type IHttpRequestBodyDetectionFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestBodyDetectionFeature);
1213
private static readonly Type IHttpResponseFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpResponseFeature);
1314
private static readonly Type IHttpResponseBodyFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpResponseBodyFeature);
1415
private static readonly Type IHttpRequestIdentifierFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestIdentifierFeature);
@@ -32,6 +33,7 @@ internal partial class IISHttpContext
3233
private static readonly Type IHttpResetFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpResetFeature);
3334

3435
private object _currentIHttpRequestFeature;
36+
private object _currentIHttpRequestBodyDetectionFeature;
3537
private object _currentIHttpResponseFeature;
3638
private object _currentIHttpResponseBodyFeature;
3739
private object _currentIHttpRequestIdentifierFeature;
@@ -56,6 +58,7 @@ internal partial class IISHttpContext
5658
private void Initialize()
5759
{
5860
_currentIHttpRequestFeature = this;
61+
_currentIHttpRequestBodyDetectionFeature = this;
5962
_currentIHttpResponseFeature = this;
6063
_currentIHttpResponseBodyFeature = this;
6164
_currentIHttpUpgradeFeature = this;
@@ -77,6 +80,10 @@ internal object FastFeatureGet(Type key)
7780
{
7881
return _currentIHttpRequestFeature;
7982
}
83+
if (key == IHttpRequestBodyDetectionFeature)
84+
{
85+
return _currentIHttpRequestBodyDetectionFeature;
86+
}
8087
if (key == IHttpResponseFeatureType)
8188
{
8289
return _currentIHttpResponseFeature;
@@ -174,6 +181,11 @@ internal void FastFeatureSet(Type key, object feature)
174181
_currentIHttpRequestFeature = feature;
175182
return;
176183
}
184+
if (key == IHttpRequestBodyDetectionFeature)
185+
{
186+
_currentIHttpRequestBodyDetectionFeature = feature;
187+
return;
188+
}
177189
if (key == IHttpResponseFeatureType)
178190
{
179191
_currentIHttpResponseFeature = feature;
@@ -284,6 +296,10 @@ private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
284296
{
285297
yield return new KeyValuePair<Type, object>(IHttpRequestFeatureType, _currentIHttpRequestFeature as global::Microsoft.AspNetCore.Http.Features.IHttpRequestFeature);
286298
}
299+
if (_currentIHttpRequestBodyDetectionFeature != null)
300+
{
301+
yield return new KeyValuePair<Type, object>(IHttpRequestBodyDetectionFeature, _currentIHttpRequestBodyDetectionFeature as global::Microsoft.AspNetCore.Http.Features.IHttpRequestBodyDetectionFeature);
302+
}
287303
if (_currentIHttpResponseFeature != null)
288304
{
289305
yield return new KeyValuePair<Type, object>(IHttpResponseFeatureType, _currentIHttpResponseFeature as global::Microsoft.AspNetCore.Http.Features.IHttpResponseFeature);

src/Servers/IIS/IIS/src/Core/IISHttpContext.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ internal unsafe IISHttpContext(
108108
public string TraceIdentifier { get; set; }
109109
public ClaimsPrincipal User { get; set; }
110110
internal WindowsPrincipal WindowsUser { get; set; }
111+
internal bool RequestCanHaveBody { get; private set; }
111112
public Stream RequestBody { get; set; }
112113
public Stream ResponseBody { get; set; }
113114
public PipeWriter ResponsePipeWrapper { get; set; }
@@ -165,6 +166,8 @@ protected void InitializeContext()
165166
RequestHeaders = new RequestHeaders(this);
166167
HttpResponseHeaders = new HeaderCollection();
167168
ResponseHeaders = HttpResponseHeaders;
169+
// Request headers can be modified by the app, read these first.
170+
RequestCanHaveBody = CheckRequestCanHaveBody();
168171

169172
if (_options.ForwardWindowsAuthentication)
170173
{
@@ -250,6 +253,20 @@ public string ReasonPhrase
250253

251254
internal IISHttpServer Server => _server;
252255

256+
private bool CheckRequestCanHaveBody()
257+
{
258+
// Http/1.x requests with bodies require either a Content-Length or Transfer-Encoding header.
259+
// Note Http.Sys adds the Transfer-Encoding: chunked header to HTTP/2 requests with bodies for back compat.
260+
// Transfer-Encoding takes priority over Content-Length.
261+
string transferEncoding = RequestHeaders[HttpKnownHeaderNames.TransferEncoding];
262+
if (string.Equals("chunked", transferEncoding?.Trim(), StringComparison.OrdinalIgnoreCase))
263+
{
264+
return true;
265+
}
266+
267+
return RequestHeaders.ContentLength.GetValueOrDefault() > 0;
268+
}
269+
253270
private async Task InitializeResponse(bool flushHeaders)
254271
{
255272
await FireOnStarting();

0 commit comments

Comments
 (0)