Skip to content

Implement PipeBody Features and add to HttpContext #6394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jan 9, 2019
Merged
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
6 changes: 6 additions & 0 deletions src/Http/Http.Abstractions/src/HttpRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.IO;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing;
Expand Down Expand Up @@ -102,6 +103,11 @@ public abstract class HttpRequest
/// <returns>The RequestBody Stream.</returns>
public abstract Stream Body { get; set; }

/// <summary>
/// Gets or sets the request body pipe <see cref="PipeReader"/>.
/// </summary>
public abstract PipeReader BodyPipe { get; set; }

/// <summary>
/// Checks the Content-Type header for form types.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Http/Http.Abstractions/src/HttpResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.IO;
using System.IO.Pipelines;
using System.Threading.Tasks;

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

/// <summary>
/// Gets or sets the response body pipe <see cref="PipeWriter"/>
/// </summary>
public abstract PipeWriter BodyPipe { get; set; }

/// <summary>
/// Gets or sets the value for the <c>Content-Length</c> response header.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Microsoft.AspNetCore.Http.HttpResponse</Description>
<Reference Include="Microsoft.AspNetCore.Http.Features" />
<Reference Include="Microsoft.Extensions.ActivatorUtilities.Sources" PrivateAssets="All" />
<Reference Include="System.Text.Encodings.Web" />
<Reference Include="System.IO.Pipelines" />
</ItemGroup>

</Project>
18 changes: 18 additions & 0 deletions src/Http/Http.Features/src/IRequestBodyPipeFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.IO.Pipelines;

namespace Microsoft.AspNetCore.Http.Features
{
/// <summary>
/// Represents the HttpRequestBody as a PipeReader.
/// </summary>
public interface IRequestBodyPipeFeature
{
/// <summary>
/// A <see cref="PipeWriter"/> representing the request body, if any.
/// </summary>
PipeReader RequestBodyPipe { get; set; }
}
}
19 changes: 19 additions & 0 deletions src/Http/Http.Features/src/IResponseBodyPipeFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO.Pipelines;

namespace Microsoft.AspNetCore.Http.Features
{
/// <summary>
/// Represents the HttpResponseBody as a PipeWriter
/// </summary>
public interface IResponseBodyPipeFeature
{
/// <summary>
/// A <see cref="PipeWriter"/> representing the response body, if any.
/// </summary>
PipeWriter ResponseBodyPipe { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<ItemGroup>
<Reference Include="Microsoft.Extensions.Primitives" />
<Reference Include="System.IO.Pipelines" />
</ItemGroup>

</Project>
49 changes: 49 additions & 0 deletions src/Http/Http/src/Features/RequestBodyPipeFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO.Pipelines;

namespace Microsoft.AspNetCore.Http.Features
{
public class RequestBodyPipeFeature : IRequestBodyPipeFeature
{
private StreamPipeReader _internalPipeReader;
private PipeReader _userSetPipeReader;
private HttpContext _context;

public RequestBodyPipeFeature(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_context = context;
}

public PipeReader RequestBodyPipe
{
get
{
if (_userSetPipeReader != null)
{
return _userSetPipeReader;
}

if (_internalPipeReader == null ||
!object.ReferenceEquals(_internalPipeReader.InnerStream, _context.Request.Body))
{
_internalPipeReader = new StreamPipeReader(_context.Request.Body);
_context.Response.RegisterForDispose(_internalPipeReader);
}

return _internalPipeReader;
}
set
{
_userSetPipeReader = value ?? throw new ArgumentNullException(nameof(value));
// TODO set the request body Stream to an adapted pipe https://github.com/aspnet/AspNetCore/issues/3971
}
}
}
}
49 changes: 49 additions & 0 deletions src/Http/Http/src/Features/ResponseBodyPipeFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO.Pipelines;

namespace Microsoft.AspNetCore.Http.Features
{
public class ResponseBodyPipeFeature : IResponseBodyPipeFeature
{
private StreamPipeWriter _internalPipeWriter;
private PipeWriter _userSetPipeWriter;
private HttpContext _context;

public ResponseBodyPipeFeature(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_context = context;
}

public PipeWriter ResponseBodyPipe
{
get
{
if (_userSetPipeWriter != null)
{
return _userSetPipeWriter;
}

if (_internalPipeWriter == null ||
!object.ReferenceEquals(_internalPipeWriter.InnerStream, _context.Response.Body))
{
_internalPipeWriter = new StreamPipeWriter(_context.Response.Body);
_context.Response.RegisterForDispose(_internalPipeWriter);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have to call RegisterForDispose here because by default the DefaultHttpContext doesn't call Uninitialize on the Request and Response features. https://github.com/aspnet/AspNetCore/blob/master/src/Http/Http/src/DefaultHttpContext.cs#L197.

I'm still thinking about how to write a test for this.

}

return _internalPipeWriter;
}
set
{
_userSetPipeWriter = value ?? throw new ArgumentNullException(nameof(value));
// TODO set the response body Stream to an adapted pipe https://github.com/aspnet/AspNetCore/issues/3971
}
}
}
}
12 changes: 12 additions & 0 deletions src/Http/Http/src/Internal/DefaultHttpRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

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

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

private IRequestBodyPipeFeature RequestBodyPipeFeature =>
_features.Fetch(ref _features.Cache.BodyPipe, this.HttpContext, _newRequestBodyPipeFeature);

public override PathString PathBase
{
get { return new PathString(HttpRequestFeature.PathBase); }
Expand Down Expand Up @@ -162,13 +167,20 @@ public override RouteValueDictionary RouteValues
set { RouteValuesFeature.RouteValues = value; }
}

public override PipeReader BodyPipe
{
get { return RequestBodyPipeFeature.RequestBodyPipe; }
set { RequestBodyPipeFeature.RequestBodyPipe = value; }
}

struct FeatureInterfaces
{
public IHttpRequestFeature Request;
public IQueryFeature Query;
public IFormFeature Form;
public IRequestCookiesFeature Cookies;
public IRouteValuesFeature RouteValues;
public IRequestBodyPipeFeature BodyPipe;
}
}
}
11 changes: 11 additions & 0 deletions src/Http/Http/src/Internal/DefaultHttpResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

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

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

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

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

Expand Down Expand Up @@ -96,6 +100,12 @@ public override bool HasStarted
get { return HttpResponseFeature.HasStarted; }
}

public override PipeWriter BodyPipe
{
get { return ResponseBodyPipeFeature.ResponseBodyPipe; }
set { ResponseBodyPipeFeature.ResponseBodyPipe = value; }
}

public override void OnStarting(Func<object, Task> callback, object state)
{
if (callback == null)
Expand Down Expand Up @@ -134,6 +144,7 @@ struct FeatureInterfaces
{
public IHttpResponseFeature Response;
public IResponseCookiesFeature Cookies;
public IResponseBodyPipeFeature BodyPipe;
}
}
}
13 changes: 13 additions & 0 deletions src/Http/Http/src/Internal/ReusableHttpRequest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -18,6 +19,8 @@ public sealed class ReusableHttpRequest : HttpRequest
private readonly static Func<HttpRequest, IFormFeature> _newFormFeature = r => new FormFeature(r);
private readonly static Func<IFeatureCollection, IRequestCookiesFeature> _newRequestCookiesFeature = f => new RequestCookiesFeature(f);
private readonly static Func<IFeatureCollection, IRouteValuesFeature> _newRouteValuesFeature = f => new RouteValuesFeature();
private readonly static Func<HttpContext, IRequestBodyPipeFeature> _newRequestBodyPipeFeature = context => new RequestBodyPipeFeature(context);


private HttpContext _context;
private FeatureReferences<FeatureInterfaces> _features;
Expand Down Expand Up @@ -56,6 +59,9 @@ public void Uninitialize()
private IRouteValuesFeature RouteValuesFeature =>
_features.Fetch(ref _features.Cache.RouteValues, _newRouteValuesFeature);

private IRequestBodyPipeFeature RequestBodyPipeFeature =>
_features.Fetch(ref _features.Cache.BodyPipe, this.HttpContext, _newRequestBodyPipeFeature);

public override PathString PathBase
{
get { return new PathString(HttpRequestFeature.PathBase); }
Expand Down Expand Up @@ -161,13 +167,20 @@ public override RouteValueDictionary RouteValues
set { RouteValuesFeature.RouteValues = value; }
}

public override PipeReader BodyPipe
{
get { return RequestBodyPipeFeature.RequestBodyPipe; }
set { RequestBodyPipeFeature.RequestBodyPipe = value; }
}

struct FeatureInterfaces
{
public IHttpRequestFeature Request;
public IQueryFeature Query;
public IFormFeature Form;
public IRequestCookiesFeature Cookies;
public IRouteValuesFeature RouteValues;
public IRequestBodyPipeFeature BodyPipe;
}
}
}
12 changes: 11 additions & 1 deletion src/Http/Http/src/Internal/ReusableHttpResponse.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
Expand All @@ -13,6 +14,7 @@ public sealed class ReusableHttpResponse : HttpResponse
// Lambdas hoisted to static readonly fields to improve inlining https://github.com/dotnet/roslyn/issues/13624
private readonly static Func<IFeatureCollection, IHttpResponseFeature> _nullResponseFeature = f => null;
private readonly static Func<IFeatureCollection, IResponseCookiesFeature> _newResponseCookiesFeature = f => new ResponseCookiesFeature(f);
private readonly static Func<HttpContext, IResponseBodyPipeFeature> _newResponseBodyPipeFeature = context => new ResponseBodyPipeFeature(context);

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

private IResponseCookiesFeature ResponseCookiesFeature =>
_features.Fetch(ref _features.Cache.Cookies, _newResponseCookiesFeature);

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

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

Expand Down Expand Up @@ -90,6 +93,12 @@ public override IResponseCookies Cookies
get { return ResponseCookiesFeature.Cookies; }
}

public override PipeWriter BodyPipe
{
get { return ResponseBodyPipeFeature.ResponseBodyPipe; }
set { ResponseBodyPipeFeature.ResponseBodyPipe = value; }
}

public override bool HasStarted
{
get { return HttpResponseFeature.HasStarted; }
Expand Down Expand Up @@ -133,6 +142,7 @@ struct FeatureInterfaces
{
public IHttpResponseFeature Response;
public IResponseCookiesFeature Cookies;
public IResponseBodyPipeFeature BodyPipe;
}
}
}
Loading