#339 Reduce IFeatureCollection surface area.
This commit is contained in:
parent
5d7ec0e2c6
commit
ac945a0bcf
|
|
@ -4,15 +4,16 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Http.Features
|
||||
{
|
||||
public class FeatureCollection : IFeatureCollection
|
||||
{
|
||||
private static KeyComparer FeatureKeyComparer = new FeatureCollection.KeyComparer();
|
||||
private readonly IFeatureCollection _defaults;
|
||||
private readonly IDictionary<Type, object> _featureByFeatureType = new Dictionary<Type, object>();
|
||||
private readonly object _containerSync = new object();
|
||||
private IDictionary<Type, object> _features;
|
||||
private volatile int _containerRevision;
|
||||
|
||||
public FeatureCollection()
|
||||
|
|
@ -24,53 +25,38 @@ namespace Microsoft.AspNet.Http.Features
|
|||
_defaults = defaults;
|
||||
}
|
||||
|
||||
public object GetInterface()
|
||||
{
|
||||
return GetInterface(null);
|
||||
}
|
||||
|
||||
public object GetInterface([NotNull] Type type)
|
||||
{
|
||||
object feature;
|
||||
if (_featureByFeatureType.TryGetValue(type, out feature))
|
||||
{
|
||||
return feature;
|
||||
}
|
||||
|
||||
if (_defaults != null && _defaults.TryGetValue(type, out feature))
|
||||
{
|
||||
return feature;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void SetInterface([NotNull] Type type, object feature)
|
||||
{
|
||||
if (feature == null)
|
||||
{
|
||||
Remove(type);
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_containerSync)
|
||||
{
|
||||
_featureByFeatureType[type] = feature;
|
||||
_containerRevision++;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int Revision
|
||||
{
|
||||
get { return _containerRevision; }
|
||||
get { return _containerRevision + (_defaults?.Revision ?? 0); }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
public IEnumerator<KeyValuePair<Type, object>> GetEnumerator()
|
||||
public object this[[NotNull] Type key]
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
get
|
||||
{
|
||||
object result;
|
||||
return _features != null && _features.TryGetValue(key, out result) ? result : _defaults?[key];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
if (_features != null && _features.Remove(key))
|
||||
{
|
||||
_containerRevision++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_features == null)
|
||||
{
|
||||
_features = new Dictionary<Type, object>();
|
||||
}
|
||||
_features[key] = value;
|
||||
_containerRevision++;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
|
|
@ -78,89 +64,42 @@ namespace Microsoft.AspNet.Http.Features
|
|||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<Type, object> item)
|
||||
public IEnumerator<KeyValuePair<Type, object>> GetEnumerator()
|
||||
{
|
||||
SetInterface(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<Type, object> item)
|
||||
{
|
||||
object value;
|
||||
return TryGetValue(item.Key, out value) && Equals(item.Value, value);
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<Type, object>[] array, int arrayIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<Type, object> item)
|
||||
{
|
||||
return Contains(item) && Remove(item.Key);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool ContainsKey([NotNull] Type key)
|
||||
{
|
||||
return GetInterface(key) != null;
|
||||
}
|
||||
|
||||
public void Add([NotNull] Type key, [NotNull] object value)
|
||||
{
|
||||
if (ContainsKey(key))
|
||||
if (_features != null)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
SetInterface(key, value);
|
||||
}
|
||||
|
||||
public bool Remove([NotNull] Type key)
|
||||
{
|
||||
lock (_containerSync)
|
||||
{
|
||||
if (_featureByFeatureType.Remove(key))
|
||||
foreach (var pair in _features)
|
||||
{
|
||||
_containerRevision++;
|
||||
return true;
|
||||
yield return pair;
|
||||
}
|
||||
}
|
||||
|
||||
if (_defaults != null)
|
||||
{
|
||||
// Don't return features masked by the wrapper.
|
||||
foreach (var pair in _features == null ? _defaults : _defaults.Except(_features, FeatureKeyComparer))
|
||||
{
|
||||
yield return pair;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue([NotNull] Type key, out object value)
|
||||
public virtual void Dispose()
|
||||
{
|
||||
value = GetInterface(key);
|
||||
return value != null;
|
||||
_defaults?.Dispose();
|
||||
}
|
||||
|
||||
public object this[Type key]
|
||||
private class KeyComparer : IEqualityComparer<KeyValuePair<Type, object>>
|
||||
{
|
||||
get { return GetInterface(key); }
|
||||
set { SetInterface(key, value); }
|
||||
}
|
||||
public bool Equals(KeyValuePair<Type, object> x, KeyValuePair<Type, object> y)
|
||||
{
|
||||
return x.Key.Equals(y.Key);
|
||||
}
|
||||
|
||||
public ICollection<Type> Keys
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public ICollection<object> Values
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
public int GetHashCode(KeyValuePair<Type, object> obj)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,23 +18,19 @@ namespace Microsoft.AspNet.Http.Features
|
|||
|
||||
public T Fetch(IFeatureCollection features)
|
||||
{
|
||||
if (_revision == features.Revision) return _feature;
|
||||
object value;
|
||||
if (features.TryGetValue(typeof(T), out value))
|
||||
if (_revision == features.Revision)
|
||||
{
|
||||
_feature = (T)value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_feature = default(T);
|
||||
return _feature;
|
||||
}
|
||||
_feature = (T)features[typeof(T)];
|
||||
_revision = features.Revision;
|
||||
return _feature;
|
||||
}
|
||||
|
||||
public T Update(IFeatureCollection features, T feature)
|
||||
{
|
||||
features[typeof(T)] = _feature = feature;
|
||||
features[typeof(T)] = feature;
|
||||
_feature = feature;
|
||||
_revision = features.Revision;
|
||||
return feature;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,27 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Http.Features
|
||||
{
|
||||
public interface IFeatureCollection : IDictionary<Type, object>, IDisposable
|
||||
public interface IFeatureCollection : IEnumerable<KeyValuePair<Type, object>>, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if the collection can be modified.
|
||||
/// </summary>
|
||||
bool IsReadOnly { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Incremented for each modification and can be used verify cached results.
|
||||
/// </summary>
|
||||
int Revision { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a given feature. Setting a null value removes the feature.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns>The requested feature, or null if it is not present.</returns>
|
||||
object this[[NotNull] Type key] { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"dnxcore50": {
|
||||
"dependencies": {
|
||||
"System.Collections": "4.0.10-beta-*",
|
||||
"System.Linq": "4.0.0-beta-*",
|
||||
"System.Net.Primitives": "4.0.10-beta-*",
|
||||
"System.Net.WebSockets": "4.0.0-beta-*",
|
||||
"System.Runtime.Extensions": "4.0.10-beta-*",
|
||||
|
|
|
|||
|
|
@ -172,8 +172,7 @@ namespace Microsoft.AspNet.Http.Internal
|
|||
|
||||
public override object GetFeature(Type type)
|
||||
{
|
||||
object value;
|
||||
return _features.TryGetValue(type, out value) ? value : null;
|
||||
return _features[type];
|
||||
}
|
||||
|
||||
public override void SetFeature(Type type, object instance)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Reflection;
|
||||
|
|
@ -16,7 +16,6 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Features;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Owin
|
||||
{
|
||||
|
|
@ -111,6 +110,12 @@ namespace Microsoft.AspNet.Owin
|
|||
set { Prop(OwinConstants.RequestHeaders, value); }
|
||||
}
|
||||
|
||||
string IHttpRequestIdentifierFeature.TraceIdentifier
|
||||
{
|
||||
get { return Prop<string>(OwinConstants.RequestId); }
|
||||
set { Prop(OwinConstants.RequestId, value); }
|
||||
}
|
||||
|
||||
Stream IHttpRequestFeature.Body
|
||||
{
|
||||
get { return Prop<Stream>(OwinConstants.RequestBody); }
|
||||
|
|
@ -266,7 +271,7 @@ namespace Microsoft.AspNet.Owin
|
|||
|
||||
/// <summary>
|
||||
/// Gets or sets if the underlying server supports WebSockets. This is enabled by default.
|
||||
/// The value should be consistant across requests.
|
||||
/// The value should be consistent across requests.
|
||||
/// </summary>
|
||||
public bool SupportsWebSockets { get; set; }
|
||||
|
||||
|
|
@ -290,17 +295,25 @@ namespace Microsoft.AspNet.Owin
|
|||
return accept(context);
|
||||
}
|
||||
|
||||
// IFeatureCollection
|
||||
|
||||
public int Revision
|
||||
{
|
||||
get { return 0; } // Not modifiable
|
||||
}
|
||||
|
||||
public void Add(Type key, object value)
|
||||
public bool IsReadOnly
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool ContainsKey(Type key)
|
||||
public object this[Type key]
|
||||
{
|
||||
get { return Get(key); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
private bool SupportsInterface(Type key)
|
||||
{
|
||||
// Does this type implement the requested interface?
|
||||
if (key.GetTypeInfo().IsAssignableFrom(GetType().GetTypeInfo()))
|
||||
|
|
@ -325,136 +338,48 @@ namespace Microsoft.AspNet.Owin
|
|||
return false;
|
||||
}
|
||||
|
||||
public ICollection<Type> Keys
|
||||
public object Get(Type key)
|
||||
{
|
||||
get
|
||||
if (SupportsInterface(key))
|
||||
{
|
||||
var keys = new List<Type>()
|
||||
{
|
||||
typeof(IHttpRequestFeature),
|
||||
typeof(IHttpResponseFeature),
|
||||
typeof(IHttpConnectionFeature),
|
||||
typeof(IOwinEnvironmentFeature),
|
||||
typeof(IHttpRequestLifetimeFeature),
|
||||
typeof(IHttpAuthenticationFeature),
|
||||
};
|
||||
if (SupportsSendFile)
|
||||
{
|
||||
keys.Add(typeof(IHttpSendFileFeature));
|
||||
}
|
||||
if (SupportsClientCerts)
|
||||
{
|
||||
keys.Add(typeof(ITlsConnectionFeature));
|
||||
}
|
||||
if (SupportsWebSockets)
|
||||
{
|
||||
keys.Add(typeof(IHttpWebSocketFeature));
|
||||
}
|
||||
return keys;
|
||||
return this;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool Remove(Type key)
|
||||
public void Set(Type key, object value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public bool TryGetValue(Type key, out object value)
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
if (ContainsKey(key))
|
||||
{
|
||||
value = this;
|
||||
return true;
|
||||
}
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public ICollection<object> Values
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public object this[Type key]
|
||||
{
|
||||
get
|
||||
{
|
||||
object value;
|
||||
if (TryGetValue(key, out value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
throw new KeyNotFoundException(key.FullName);
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<Type, object> item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<Type, object> item)
|
||||
{
|
||||
object result;
|
||||
return TryGetValue(item.Key, out result) && result.Equals(item.Value);
|
||||
}
|
||||
|
||||
public void CopyTo([NotNull] KeyValuePair<Type, object>[] array, int arrayIndex)
|
||||
{
|
||||
if (arrayIndex < 0 || arrayIndex > array.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, string.Empty);
|
||||
}
|
||||
var keys = Keys;
|
||||
if (keys.Count > array.Length - arrayIndex)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
foreach (var key in keys)
|
||||
{
|
||||
array[arrayIndex++] = new KeyValuePair<Type, object>(key, this[key]);
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return Keys.Count; }
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
string IHttpRequestIdentifierFeature.TraceIdentifier
|
||||
{
|
||||
get { return Prop<string>(OwinConstants.RequestId); }
|
||||
set { Prop(OwinConstants.RequestId, value); }
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<Type, object> item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<Type, object>> GetEnumerator()
|
||||
{
|
||||
return Keys.Select(type => new KeyValuePair<Type, object>(type, this[type])).GetEnumerator();
|
||||
}
|
||||
yield return new KeyValuePair<Type, object>(typeof(IHttpRequestFeature), this);
|
||||
yield return new KeyValuePair<Type, object>(typeof(IHttpResponseFeature), this);
|
||||
yield return new KeyValuePair<Type, object>(typeof(IHttpConnectionFeature), this);
|
||||
yield return new KeyValuePair<Type, object>(typeof(IHttpRequestIdentifierFeature), this);
|
||||
yield return new KeyValuePair<Type, object>(typeof(IHttpRequestLifetimeFeature), this);
|
||||
yield return new KeyValuePair<Type, object>(typeof(IHttpAuthenticationFeature), this);
|
||||
yield return new KeyValuePair<Type, object>(typeof(IOwinEnvironmentFeature), this);
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
// Check for conditional features
|
||||
if (SupportsSendFile)
|
||||
{
|
||||
yield return new KeyValuePair<Type, object>(typeof(IHttpSendFileFeature), this);
|
||||
}
|
||||
if (SupportsClientCerts)
|
||||
{
|
||||
yield return new KeyValuePair<Type, object>(typeof(ITlsConnectionFeature), this);
|
||||
}
|
||||
if (SupportsWebSockets)
|
||||
{
|
||||
yield return new KeyValuePair<Type, object>(typeof(IHttpWebSocketFeature), this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Http.Features
|
||||
{
|
||||
public class FeatureCollectionTests
|
||||
{
|
||||
[Fact]
|
||||
public void AddedInterfaceIsReturned()
|
||||
{
|
||||
var interfaces = new FeatureCollection();
|
||||
var thing = new Thing();
|
||||
|
||||
interfaces[typeof(IThing)] = thing;
|
||||
|
||||
object thing2 = interfaces[typeof(IThing)];
|
||||
Assert.Equal(thing2, thing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IndexerAlsoAddsItems()
|
||||
{
|
||||
var interfaces = new FeatureCollection();
|
||||
var thing = new Thing();
|
||||
|
||||
interfaces[typeof(IThing)] = thing;
|
||||
|
||||
Assert.Equal(interfaces[typeof(IThing)], thing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetNullValueRemoves()
|
||||
{
|
||||
var interfaces = new FeatureCollection();
|
||||
var thing = new Thing();
|
||||
|
||||
interfaces[typeof(IThing)] = thing;
|
||||
Assert.Equal(interfaces[typeof(IThing)], thing);
|
||||
|
||||
interfaces[typeof(IThing)] = null;
|
||||
|
||||
object thing2 = interfaces[typeof(IThing)];
|
||||
Assert.Null(thing2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Http.Features
|
||||
{
|
||||
public class InterfaceDictionaryTests
|
||||
{
|
||||
[Fact]
|
||||
public void AddedInterfaceIsReturned()
|
||||
{
|
||||
var interfaces = new FeatureCollection();
|
||||
var thing = new Thing();
|
||||
|
||||
interfaces.Add(typeof(IThing), thing);
|
||||
|
||||
Assert.Equal(interfaces[typeof(IThing)], thing);
|
||||
|
||||
object thing2;
|
||||
Assert.True(interfaces.TryGetValue(typeof(IThing), out thing2));
|
||||
Assert.Equal(thing2, thing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IndexerAlsoAddsItems()
|
||||
{
|
||||
var interfaces = new FeatureCollection();
|
||||
var thing = new Thing();
|
||||
|
||||
interfaces[typeof(IThing)] = thing;
|
||||
|
||||
Assert.Equal(interfaces[typeof(IThing)], thing);
|
||||
|
||||
object thing2;
|
||||
Assert.True(interfaces.TryGetValue(typeof(IThing), out thing2));
|
||||
Assert.Equal(thing2, thing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SecondCallToAddThrowsException()
|
||||
{
|
||||
var interfaces = new FeatureCollection();
|
||||
var thing = new Thing();
|
||||
|
||||
interfaces.Add(typeof(IThing), thing);
|
||||
|
||||
Assert.Throws<ArgumentException>(() => interfaces.Add(typeof(IThing), thing));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemovedInterfaceIsRemoved()
|
||||
{
|
||||
var interfaces = new FeatureCollection();
|
||||
var thing = new Thing();
|
||||
|
||||
interfaces.Add(typeof(IThing), thing);
|
||||
|
||||
Assert.Equal(interfaces[typeof(IThing)], thing);
|
||||
|
||||
Assert.True(interfaces.Remove(typeof(IThing)));
|
||||
|
||||
object thing2;
|
||||
Assert.False(interfaces.TryGetValue(typeof(IThing), out thing2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetNullValueRemoves()
|
||||
{
|
||||
var interfaces = new FeatureCollection();
|
||||
var thing = new Thing();
|
||||
|
||||
interfaces.Add(typeof(IThing), thing);
|
||||
Assert.Equal(interfaces[typeof(IThing)], thing);
|
||||
|
||||
interfaces[typeof(IThing)] = null;
|
||||
|
||||
object thing2;
|
||||
Assert.False(interfaces.TryGetValue(typeof(IThing), out thing2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
|||
var features = new FeatureCollection();
|
||||
var request = new HttpRequestFeature();
|
||||
request.QueryString = "foo=bar";
|
||||
features.Add(typeof(IHttpRequestFeature), request);
|
||||
features[typeof(IHttpRequestFeature)] = request;
|
||||
|
||||
var provider = new QueryFeature(features);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ namespace Microsoft.AspNet.Owin
|
|||
{
|
||||
private T Get<T>(IFeatureCollection features)
|
||||
{
|
||||
object value;
|
||||
return features.TryGetValue(typeof(T), out value) ? (T)value : default(T);
|
||||
return (T)features[typeof(T)];
|
||||
}
|
||||
|
||||
private T Get<T>(IDictionary<string, object> env, string key)
|
||||
{
|
||||
object value;
|
||||
|
|
@ -63,22 +63,6 @@ namespace Microsoft.AspNet.Owin
|
|||
Assert.Equal("/pathBase2", Get<string>(env, "owin.RequestPathBase"));
|
||||
Assert.Equal("name=value2", Get<string>(env, "owin.RequestQueryString"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ImplementedInterfacesAreEnumerated()
|
||||
{
|
||||
var env = new Dictionary<string, object>
|
||||
{
|
||||
{"owin.RequestMethod", "POST"}
|
||||
};
|
||||
var features = new OwinFeatureCollection(env);
|
||||
|
||||
var entries = features.ToArray();
|
||||
var keys = features.Keys.ToArray();
|
||||
|
||||
Assert.Contains(typeof(IHttpRequestFeature), keys);
|
||||
Assert.Contains(typeof(IHttpResponseFeature), keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue