diff --git a/src/Microsoft.AspNet.Http.Abstractions/HttpContext.cs b/src/Microsoft.AspNet.Http.Abstractions/HttpContext.cs
index c7a63c83..4e89caed 100644
--- a/src/Microsoft.AspNet.Http.Abstractions/HttpContext.cs
+++ b/src/Microsoft.AspNet.Http.Abstractions/HttpContext.cs
@@ -12,6 +12,8 @@ namespace Microsoft.AspNet.Http
{
public abstract class HttpContext : IDisposable
{
+ public abstract IFeatureCollection Features { get; }
+
public abstract HttpRequest Request { get; }
public abstract HttpResponse Response { get; }
diff --git a/src/Microsoft.AspNet.Http.Extensions/SendFileResponseExtensions.cs b/src/Microsoft.AspNet.Http.Extensions/SendFileResponseExtensions.cs
index 3c5f183c..b60f1aca 100644
--- a/src/Microsoft.AspNet.Http.Extensions/SendFileResponseExtensions.cs
+++ b/src/Microsoft.AspNet.Http.Extensions/SendFileResponseExtensions.cs
@@ -22,7 +22,7 @@ public static class SendFileResponseExtensions
/// True if sendfile feature exists in the response.
public static bool SupportsSendFile([NotNull] this HttpResponse response)
{
- return response.HttpContext.GetFeature() != null;
+ return response.HttpContext.Features.Get() != null;
}
///
@@ -47,7 +47,7 @@ public static Task SendFileAsync([NotNull] this HttpResponse response, [NotNull]
///
public static Task SendFileAsync([NotNull] this HttpResponse response, [NotNull] string fileName, long offset, long? count, CancellationToken cancellationToken)
{
- var sendFile = response.HttpContext.GetFeature();
+ var sendFile = response.HttpContext.Features.Get();
if (sendFile == null)
{
throw new NotSupportedException(Resources.Exception_SendFileNotSupported);
diff --git a/src/Microsoft.AspNet.Http.Features/FeatureCollectionExtensions.cs b/src/Microsoft.AspNet.Http.Features/FeatureCollectionExtensions.cs
new file mode 100644
index 00000000..86681fde
--- /dev/null
+++ b/src/Microsoft.AspNet.Http.Features/FeatureCollectionExtensions.cs
@@ -0,0 +1,30 @@
+// 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.
+
+namespace Microsoft.AspNet.Http.Features
+{
+ public static class FeatureCollectionExtensions
+ {
+ ///
+ /// Retrieves the requested feature from the collection.
+ ///
+ /// The feature key.
+ /// The collection.
+ /// The requested feature, or null if it is not present.
+ public static TFeature Get(this IFeatureCollection features)
+ {
+ return (TFeature)features[typeof(TFeature)];
+ }
+
+ ///
+ /// Sets the given feature in the collection.
+ ///
+ /// The feature key.
+ /// The collection.
+ /// The feature value.
+ public static void Set(this IFeatureCollection features, TFeature instance)
+ {
+ features[typeof(TFeature)] = instance;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Http/DefaultHttpContext.cs b/src/Microsoft.AspNet.Http/DefaultHttpContext.cs
index c3caf8ec..fd9ad194 100644
--- a/src/Microsoft.AspNet.Http/DefaultHttpContext.cs
+++ b/src/Microsoft.AspNet.Http/DefaultHttpContext.cs
@@ -32,8 +32,8 @@ public class DefaultHttpContext : HttpContext
public DefaultHttpContext()
: this(new FeatureCollection())
{
- SetFeature(new HttpRequestFeature());
- SetFeature(new HttpResponseFeature());
+ _features.Set(new HttpRequestFeature());
+ _features.Set(new HttpResponseFeature());
}
public DefaultHttpContext(IFeatureCollection features)
@@ -76,6 +76,8 @@ private ISessionFeature SessionFeature
get { return _session.Fetch(_features); }
}
+ public override IFeatureCollection Features { get { return _features; } }
+
public override HttpRequest Request { get { return _request; } }
public override HttpResponse Response { get { return _response; } }
diff --git a/src/Microsoft.AspNet.Owin/OwinEnvironment.cs b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs
index 62ca09b8..c6cdd639 100644
--- a/src/Microsoft.AspNet.Owin/OwinEnvironment.cs
+++ b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs
@@ -37,11 +37,11 @@ public class OwinEnvironment : IDictionary
public OwinEnvironment(HttpContext context)
{
- if (context.GetFeature() == null)
+ if (context.Features.Get() == null)
{
throw new ArgumentException("Missing required feature: " + nameof(IHttpRequestFeature) + ".", nameof(context));
}
- if (context.GetFeature() == null)
+ if (context.Features.Get() == null)
{
throw new ArgumentException("Missing required feature: " + nameof(IHttpResponseFeature) + ".", nameof(context));
}
@@ -100,7 +100,7 @@ public OwinEnvironment(HttpContext context)
};
// owin.CallCancelled is required but the feature may not be present.
- if (context.GetFeature() != null)
+ if (context.Features.Get() != null)
{
_entries[OwinConstants.CallCancelled] = new FeatureMap(feature => feature.RequestAborted);
}
@@ -334,7 +334,7 @@ public bool CanSet
internal bool TryGet(HttpContext context, out object value)
{
- object featureInstance = context.GetFeature(FeatureInterface);
+ object featureInstance = context.Features[FeatureInterface];
if (featureInstance == null)
{
value = null;
@@ -350,7 +350,7 @@ internal bool TryGet(HttpContext context, out object value)
internal void Set(HttpContext context, object value)
{
- var feature = context.GetFeature(FeatureInterface);
+ var feature = context.Features[FeatureInterface];
if (feature == null)
{
if (FeatureFactory == null)
@@ -360,7 +360,7 @@ internal void Set(HttpContext context, object value)
else
{
feature = FeatureFactory();
- context.SetFeature(FeatureInterface, feature);
+ context.Features[FeatureInterface] = feature;
}
}
Setter(feature, value);
diff --git a/src/Microsoft.AspNet.Owin/OwinExtensions.cs b/src/Microsoft.AspNet.Owin/OwinExtensions.cs
index 770f02fb..3f31ce59 100644
--- a/src/Microsoft.AspNet.Owin/OwinExtensions.cs
+++ b/src/Microsoft.AspNet.Owin/OwinExtensions.cs
@@ -39,7 +39,7 @@ public static AddMiddleware UseOwin(this IApplicationBuilder builder)
{
// Use the existing OWIN env if there is one.
IDictionary env;
- var owinEnvFeature = httpContext.GetFeature();
+ var owinEnvFeature = httpContext.Features.Get();
if (owinEnvFeature != null)
{
env = owinEnvFeature.Environment;
@@ -87,7 +87,7 @@ private static CreateMiddleware CreateMiddlewareFactory(Func
{
- return next(httpContext.GetFeature().Environment);
+ return next(httpContext.Features.Get().Environment);
});
return env =>
@@ -98,7 +98,7 @@ private static CreateMiddleware CreateMiddlewareFactory(Func(new OwinEnvironmentFeature() { Environment = env });
+ context.Features.Set(new OwinEnvironmentFeature() { Environment = env });
}
else
{
diff --git a/test/Microsoft.AspNet.Http.Extensions.Tests/SendFileResponseExtensionsTests.cs b/test/Microsoft.AspNet.Http.Extensions.Tests/SendFileResponseExtensionsTests.cs
index ef8aa04c..cd16acb0 100644
--- a/test/Microsoft.AspNet.Http.Extensions.Tests/SendFileResponseExtensionsTests.cs
+++ b/test/Microsoft.AspNet.Http.Extensions.Tests/SendFileResponseExtensionsTests.cs
@@ -17,7 +17,7 @@ public void SendFileSupport()
var context = new DefaultHttpContext();
var response = context.Response;
Assert.False(response.SupportsSendFile());
- context.SetFeature(new FakeSendFileFeature());
+ context.Features.Set(new FakeSendFileFeature());
Assert.True(response.SupportsSendFile());
}
@@ -34,7 +34,7 @@ public async Task SendFileWorks()
var context = new DefaultHttpContext();
var response = context.Response;
var fakeFeature = new FakeSendFileFeature();
- context.SetFeature(fakeFeature);
+ context.Features.Set(fakeFeature);
await response.SendFileAsync("bob", 1, 3, CancellationToken.None);
diff --git a/test/Microsoft.AspNet.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs b/test/Microsoft.AspNet.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs
index 3430382e..db7e1f7f 100644
--- a/test/Microsoft.AspNet.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs
+++ b/test/Microsoft.AspNet.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs
@@ -4,6 +4,7 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks;
+using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Authentication;
using Microsoft.AspNet.Http.Features.Authentication.Internal;
using Microsoft.AspNet.Http.Internal;
@@ -50,7 +51,7 @@ public async Task SignInOutIn()
{
var context = CreateContext();
var handler = new AuthHandler();
- context.SetFeature(new HttpAuthenticationFeature() { Handler = handler });
+ context.Features.Set(new HttpAuthenticationFeature() { Handler = handler });
var user = new ClaimsPrincipal();
await context.Authentication.SignInAsync("ignored", user);
Assert.True(handler.SignedIn);
diff --git a/test/Microsoft.AspNet.Http.Tests/DefaultHttpContextTests.cs b/test/Microsoft.AspNet.Http.Tests/DefaultHttpContextTests.cs
index f24035e6..b8c9691e 100644
--- a/test/Microsoft.AspNet.Http.Tests/DefaultHttpContextTests.cs
+++ b/test/Microsoft.AspNet.Http.Tests/DefaultHttpContextTests.cs
@@ -35,7 +35,7 @@ public void GetOnSessionProperty_ReturnsAvailableSession()
session.Set("key2", null);
var feature = new BlahSessionFeature();
feature.Session = session;
- context.SetFeature(feature);
+ context.Features.Set(feature);
// Act & Assert
Assert.Same(session, context.Session);
@@ -67,7 +67,7 @@ public void SettingSession_OverridesAvailableSession()
session.Set("key2", null);
var feature = new BlahSessionFeature();
feature.Session = session;
- context.SetFeature(feature);
+ context.Features.Set(feature);
// Act
context.Session = new TestSession();
@@ -109,9 +109,9 @@ public void EmptyUserIsNeverNull()
public void GetItems_DefaultCollectionProvided()
{
var context = new DefaultHttpContext(new FeatureCollection());
- Assert.Null(context.GetFeature());
+ Assert.Null(context.Features.Get());
var items = context.Items;
- Assert.NotNull(context.GetFeature());
+ Assert.NotNull(context.Features.Get());
Assert.NotNull(items);
Assert.Same(items, context.Items);
var item = new object();
@@ -123,10 +123,10 @@ public void GetItems_DefaultCollectionProvided()
public void SetItems_NewCollectionUsed()
{
var context = new DefaultHttpContext(new FeatureCollection());
- Assert.Null(context.GetFeature());
+ Assert.Null(context.Features.Get());
var items = new Dictionary