Skip to content

Commit 5541a7a

Browse files
authored
Implement PipeBody Features and add to HttpContext (#6394)
1 parent e899823 commit 5541a7a

23 files changed

+639
-12
lines changed

src/Http/Http.Abstractions/src/HttpRequest.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System.IO;
5+
using System.IO.Pipelines;
56
using System.Threading;
67
using System.Threading.Tasks;
78
using Microsoft.AspNetCore.Routing;
@@ -102,6 +103,11 @@ public abstract class HttpRequest
102103
/// <returns>The RequestBody Stream.</returns>
103104
public abstract Stream Body { get; set; }
104105

106+
/// <summary>
107+
/// Gets or sets the request body pipe <see cref="PipeReader"/>.
108+
/// </summary>
109+
public abstract PipeReader BodyPipe { get; set; }
110+
105111
/// <summary>
106112
/// Checks the Content-Type header for form types.
107113
/// </summary>

src/Http/Http.Abstractions/src/HttpResponse.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.IO;
6+
using System.IO.Pipelines;
67
using System.Threading.Tasks;
78

89
namespace Microsoft.AspNetCore.Http
@@ -39,6 +40,11 @@ public abstract class HttpResponse
3940
/// </summary>
4041
public abstract Stream Body { get; set; }
4142

43+
/// <summary>
44+
/// Gets or sets the response body pipe <see cref="PipeWriter"/>
45+
/// </summary>
46+
public abstract PipeWriter BodyPipe { get; set; }
47+
4248
/// <summary>
4349
/// Gets or sets the value for the <c>Content-Length</c> response header.
4450
/// </summary>

src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Microsoft.AspNetCore.Http.HttpResponse</Description>
2222
<Reference Include="Microsoft.AspNetCore.Http.Features" />
2323
<Reference Include="Microsoft.Extensions.ActivatorUtilities.Sources" PrivateAssets="All" />
2424
<Reference Include="System.Text.Encodings.Web" />
25+
<Reference Include="System.IO.Pipelines" />
2526
</ItemGroup>
2627

2728
</Project>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.IO.Pipelines;
5+
6+
namespace Microsoft.AspNetCore.Http.Features
7+
{
8+
/// <summary>
9+
/// Represents the HttpRequestBody as a PipeReader.
10+
/// </summary>
11+
public interface IRequestBodyPipeFeature
12+
{
13+
/// <summary>
14+
/// A <see cref="PipeWriter"/> representing the request body, if any.
15+
/// </summary>
16+
PipeReader RequestBodyPipe { get; set; }
17+
}
18+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.IO.Pipelines;
6+
7+
namespace Microsoft.AspNetCore.Http.Features
8+
{
9+
/// <summary>
10+
/// Represents the HttpResponseBody as a PipeWriter
11+
/// </summary>
12+
public interface IResponseBodyPipeFeature
13+
{
14+
/// <summary>
15+
/// A <see cref="PipeWriter"/> representing the response body, if any.
16+
/// </summary>
17+
PipeWriter ResponseBodyPipe { get; set; }
18+
}
19+
}

src/Http/Http.Features/src/Microsoft.AspNetCore.Http.Features.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
<ItemGroup>
1313
<Reference Include="Microsoft.Extensions.Primitives" />
14+
<Reference Include="System.IO.Pipelines" />
1415
</ItemGroup>
1516

1617
</Project>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.IO.Pipelines;
6+
7+
namespace Microsoft.AspNetCore.Http.Features
8+
{
9+
public class RequestBodyPipeFeature : IRequestBodyPipeFeature
10+
{
11+
private StreamPipeReader _internalPipeReader;
12+
private PipeReader _userSetPipeReader;
13+
private HttpContext _context;
14+
15+
public RequestBodyPipeFeature(HttpContext context)
16+
{
17+
if (context == null)
18+
{
19+
throw new ArgumentNullException(nameof(context));
20+
}
21+
_context = context;
22+
}
23+
24+
public PipeReader RequestBodyPipe
25+
{
26+
get
27+
{
28+
if (_userSetPipeReader != null)
29+
{
30+
return _userSetPipeReader;
31+
}
32+
33+
if (_internalPipeReader == null ||
34+
!object.ReferenceEquals(_internalPipeReader.InnerStream, _context.Request.Body))
35+
{
36+
_internalPipeReader = new StreamPipeReader(_context.Request.Body);
37+
_context.Response.RegisterForDispose(_internalPipeReader);
38+
}
39+
40+
return _internalPipeReader;
41+
}
42+
set
43+
{
44+
_userSetPipeReader = value ?? throw new ArgumentNullException(nameof(value));
45+
// TODO set the request body Stream to an adapted pipe https://github.com/aspnet/AspNetCore/issues/3971
46+
}
47+
}
48+
}
49+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.IO.Pipelines;
6+
7+
namespace Microsoft.AspNetCore.Http.Features
8+
{
9+
public class ResponseBodyPipeFeature : IResponseBodyPipeFeature
10+
{
11+
private StreamPipeWriter _internalPipeWriter;
12+
private PipeWriter _userSetPipeWriter;
13+
private HttpContext _context;
14+
15+
public ResponseBodyPipeFeature(HttpContext context)
16+
{
17+
if (context == null)
18+
{
19+
throw new ArgumentNullException(nameof(context));
20+
}
21+
_context = context;
22+
}
23+
24+
public PipeWriter ResponseBodyPipe
25+
{
26+
get
27+
{
28+
if (_userSetPipeWriter != null)
29+
{
30+
return _userSetPipeWriter;
31+
}
32+
33+
if (_internalPipeWriter == null ||
34+
!object.ReferenceEquals(_internalPipeWriter.InnerStream, _context.Response.Body))
35+
{
36+
_internalPipeWriter = new StreamPipeWriter(_context.Response.Body);
37+
_context.Response.RegisterForDispose(_internalPipeWriter);
38+
}
39+
40+
return _internalPipeWriter;
41+
}
42+
set
43+
{
44+
_userSetPipeWriter = value ?? throw new ArgumentNullException(nameof(value));
45+
// TODO set the response body Stream to an adapted pipe https://github.com/aspnet/AspNetCore/issues/3971
46+
}
47+
}
48+
}
49+
}

src/Http/Http/src/Internal/DefaultHttpRequest.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.IO;
6+
using System.IO.Pipelines;
67
using System.Threading;
78
using System.Threading.Tasks;
89
using Microsoft.AspNetCore.Http.Features;
@@ -19,6 +20,7 @@ public class DefaultHttpRequest : HttpRequest
1920
private readonly static Func<HttpRequest, IFormFeature> _newFormFeature = r => new FormFeature(r);
2021
private readonly static Func<IFeatureCollection, IRequestCookiesFeature> _newRequestCookiesFeature = f => new RequestCookiesFeature(f);
2122
private readonly static Func<IFeatureCollection, IRouteValuesFeature> _newRouteValuesFeature = f => new RouteValuesFeature();
23+
private readonly static Func<HttpContext, IRequestBodyPipeFeature> _newRequestBodyPipeFeature = context => new RequestBodyPipeFeature(context);
2224

2325
private HttpContext _context;
2426
private FeatureReferences<FeatureInterfaces> _features;
@@ -57,6 +59,9 @@ public virtual void Uninitialize()
5759
private IRouteValuesFeature RouteValuesFeature =>
5860
_features.Fetch(ref _features.Cache.RouteValues, _newRouteValuesFeature);
5961

62+
private IRequestBodyPipeFeature RequestBodyPipeFeature =>
63+
_features.Fetch(ref _features.Cache.BodyPipe, this.HttpContext, _newRequestBodyPipeFeature);
64+
6065
public override PathString PathBase
6166
{
6267
get { return new PathString(HttpRequestFeature.PathBase); }
@@ -162,13 +167,20 @@ public override RouteValueDictionary RouteValues
162167
set { RouteValuesFeature.RouteValues = value; }
163168
}
164169

170+
public override PipeReader BodyPipe
171+
{
172+
get { return RequestBodyPipeFeature.RequestBodyPipe; }
173+
set { RequestBodyPipeFeature.RequestBodyPipe = value; }
174+
}
175+
165176
struct FeatureInterfaces
166177
{
167178
public IHttpRequestFeature Request;
168179
public IQueryFeature Query;
169180
public IFormFeature Form;
170181
public IRequestCookiesFeature Cookies;
171182
public IRouteValuesFeature RouteValues;
183+
public IRequestBodyPipeFeature BodyPipe;
172184
}
173185
}
174186
}

src/Http/Http/src/Internal/DefaultHttpResponse.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.IO;
6+
using System.IO.Pipelines;
67
using System.Threading.Tasks;
78
using Microsoft.AspNetCore.Http.Features;
89
using Microsoft.Net.Http.Headers;
@@ -14,6 +15,7 @@ public class DefaultHttpResponse : HttpResponse
1415
// Lambdas hoisted to static readonly fields to improve inlining https://github.com/dotnet/roslyn/issues/13624
1516
private readonly static Func<IFeatureCollection, IHttpResponseFeature> _nullResponseFeature = f => null;
1617
private readonly static Func<IFeatureCollection, IResponseCookiesFeature> _newResponseCookiesFeature = f => new ResponseCookiesFeature(f);
18+
private readonly static Func<HttpContext, IResponseBodyPipeFeature> _newResponseBodyPipeFeature = context => new ResponseBodyPipeFeature(context);
1719

1820
private HttpContext _context;
1921
private FeatureReferences<FeatureInterfaces> _features;
@@ -41,6 +43,8 @@ public virtual void Uninitialize()
4143
private IResponseCookiesFeature ResponseCookiesFeature =>
4244
_features.Fetch(ref _features.Cache.Cookies, _newResponseCookiesFeature);
4345

46+
private IResponseBodyPipeFeature ResponseBodyPipeFeature =>
47+
_features.Fetch(ref _features.Cache.BodyPipe, this.HttpContext, _newResponseBodyPipeFeature);
4448

4549
public override HttpContext HttpContext { get { return _context; } }
4650

@@ -96,6 +100,12 @@ public override bool HasStarted
96100
get { return HttpResponseFeature.HasStarted; }
97101
}
98102

103+
public override PipeWriter BodyPipe
104+
{
105+
get { return ResponseBodyPipeFeature.ResponseBodyPipe; }
106+
set { ResponseBodyPipeFeature.ResponseBodyPipe = value; }
107+
}
108+
99109
public override void OnStarting(Func<object, Task> callback, object state)
100110
{
101111
if (callback == null)
@@ -134,6 +144,7 @@ struct FeatureInterfaces
134144
{
135145
public IHttpResponseFeature Response;
136146
public IResponseCookiesFeature Cookies;
147+
public IResponseBodyPipeFeature BodyPipe;
137148
}
138149
}
139150
}

src/Http/Http/src/Internal/ReusableHttpRequest.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.IO.Pipelines;
45
using System.Text;
56
using System.Threading;
67
using System.Threading.Tasks;
@@ -18,6 +19,8 @@ public sealed class ReusableHttpRequest : HttpRequest
1819
private readonly static Func<HttpRequest, IFormFeature> _newFormFeature = r => new FormFeature(r);
1920
private readonly static Func<IFeatureCollection, IRequestCookiesFeature> _newRequestCookiesFeature = f => new RequestCookiesFeature(f);
2021
private readonly static Func<IFeatureCollection, IRouteValuesFeature> _newRouteValuesFeature = f => new RouteValuesFeature();
22+
private readonly static Func<HttpContext, IRequestBodyPipeFeature> _newRequestBodyPipeFeature = context => new RequestBodyPipeFeature(context);
23+
2124

2225
private HttpContext _context;
2326
private FeatureReferences<FeatureInterfaces> _features;
@@ -56,6 +59,9 @@ public void Uninitialize()
5659
private IRouteValuesFeature RouteValuesFeature =>
5760
_features.Fetch(ref _features.Cache.RouteValues, _newRouteValuesFeature);
5861

62+
private IRequestBodyPipeFeature RequestBodyPipeFeature =>
63+
_features.Fetch(ref _features.Cache.BodyPipe, this.HttpContext, _newRequestBodyPipeFeature);
64+
5965
public override PathString PathBase
6066
{
6167
get { return new PathString(HttpRequestFeature.PathBase); }
@@ -161,13 +167,20 @@ public override RouteValueDictionary RouteValues
161167
set { RouteValuesFeature.RouteValues = value; }
162168
}
163169

170+
public override PipeReader BodyPipe
171+
{
172+
get { return RequestBodyPipeFeature.RequestBodyPipe; }
173+
set { RequestBodyPipeFeature.RequestBodyPipe = value; }
174+
}
175+
164176
struct FeatureInterfaces
165177
{
166178
public IHttpRequestFeature Request;
167179
public IQueryFeature Query;
168180
public IFormFeature Form;
169181
public IRequestCookiesFeature Cookies;
170182
public IRouteValuesFeature RouteValues;
183+
public IRequestBodyPipeFeature BodyPipe;
171184
}
172185
}
173186
}

src/Http/Http/src/Internal/ReusableHttpResponse.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.IO.Pipelines;
45
using System.Text;
56
using System.Threading.Tasks;
67
using Microsoft.AspNetCore.Http.Features;
@@ -13,6 +14,7 @@ public sealed class ReusableHttpResponse : HttpResponse
1314
// Lambdas hoisted to static readonly fields to improve inlining https://github.com/dotnet/roslyn/issues/13624
1415
private readonly static Func<IFeatureCollection, IHttpResponseFeature> _nullResponseFeature = f => null;
1516
private readonly static Func<IFeatureCollection, IResponseCookiesFeature> _newResponseCookiesFeature = f => new ResponseCookiesFeature(f);
17+
private readonly static Func<HttpContext, IResponseBodyPipeFeature> _newResponseBodyPipeFeature = context => new ResponseBodyPipeFeature(context);
1618

1719
private HttpContext _context;
1820
private FeatureReferences<FeatureInterfaces> _features;
@@ -39,7 +41,8 @@ public void Uninitialize()
3941

4042
private IResponseCookiesFeature ResponseCookiesFeature =>
4143
_features.Fetch(ref _features.Cache.Cookies, _newResponseCookiesFeature);
42-
44+
private IResponseBodyPipeFeature ResponseBodyPipeFeature =>
45+
_features.Fetch(ref _features.Cache.BodyPipe, this.HttpContext, _newResponseBodyPipeFeature);
4346

4447
public override HttpContext HttpContext { get { return _context; } }
4548

@@ -90,6 +93,12 @@ public override IResponseCookies Cookies
9093
get { return ResponseCookiesFeature.Cookies; }
9194
}
9295

96+
public override PipeWriter BodyPipe
97+
{
98+
get { return ResponseBodyPipeFeature.ResponseBodyPipe; }
99+
set { ResponseBodyPipeFeature.ResponseBodyPipe = value; }
100+
}
101+
93102
public override bool HasStarted
94103
{
95104
get { return HttpResponseFeature.HasStarted; }
@@ -133,6 +142,7 @@ struct FeatureInterfaces
133142
{
134143
public IHttpResponseFeature Response;
135144
public IResponseCookiesFeature Cookies;
145+
public IResponseBodyPipeFeature BodyPipe;
136146
}
137147
}
138148
}

0 commit comments

Comments
 (0)