diff --git a/src/Microsoft.AspNet.Http.Abstractions/IHeaderDictionary.cs b/src/Microsoft.AspNet.Http.Abstractions/IHeaderDictionary.cs
deleted file mode 100644
index b6483c15..00000000
--- a/src/Microsoft.AspNet.Http.Abstractions/IHeaderDictionary.cs
+++ /dev/null
@@ -1,34 +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.Collections.Generic;
-using Microsoft.Extensions.Primitives;
-
-namespace Microsoft.AspNet.Http
-{
- ///
- /// Represents request and response headers
- ///
- public interface IHeaderDictionary : IReadableStringCollection, IDictionary
- {
- // This property is duplicated to resolve an ambiguity between IReadableStringCollection and IDictionary
- ///
- ///
- ///
- ///
- /// The stored value, or StringValues.Empty if the key is not present.
- new StringValues this[string key] { get; set; }
-
- // This property is duplicated to resolve an ambiguity between IReadableStringCollection.Count and IDictionary.Count
- ///
- /// Gets the number of elements contained in the collection.
- ///
- new int Count { get; }
-
- // This property is duplicated to resolve an ambiguity between IReadableStringCollection.Keys and IDictionary.Keys
- ///
- /// Gets a collection containing the keys.
- ///
- new ICollection Keys { get; }
- }
-}
diff --git a/src/Microsoft.AspNet.Http.Features/IHeaderDictionary.cs b/src/Microsoft.AspNet.Http.Features/IHeaderDictionary.cs
new file mode 100644
index 00000000..0c03c29d
--- /dev/null
+++ b/src/Microsoft.AspNet.Http.Features/IHeaderDictionary.cs
@@ -0,0 +1,21 @@
+// 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.Collections.Generic;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNet.Http
+{
+ ///
+ /// Represents request and response headers
+ ///
+ public interface IHeaderDictionary : IDictionary
+ {
+ ///
+ /// IHeaderDictionary has a different indexer contract than IDictionary, where it will return StringValues.Empty for missing entries.
+ ///
+ ///
+ /// The stored value, or StringValues.Empty if the key is not present.
+ new StringValues this[string key] { get; set; }
+ }
+}
diff --git a/src/Microsoft.AspNet.Http.Features/IHttpRequestFeature.cs b/src/Microsoft.AspNet.Http.Features/IHttpRequestFeature.cs
index d67143e0..988f2b61 100644
--- a/src/Microsoft.AspNet.Http.Features/IHttpRequestFeature.cs
+++ b/src/Microsoft.AspNet.Http.Features/IHttpRequestFeature.cs
@@ -1,9 +1,7 @@
// 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.Collections.Generic;
using System.IO;
-using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNet.Http.Features
{
@@ -15,7 +13,7 @@ public interface IHttpRequestFeature
string PathBase { get; set; }
string Path { get; set; }
string QueryString { get; set; }
- IDictionary Headers { get; set; }
+ IHeaderDictionary Headers { get; set; }
Stream Body { get; set; }
}
}
diff --git a/src/Microsoft.AspNet.Http.Features/IHttpResponseFeature.cs b/src/Microsoft.AspNet.Http.Features/IHttpResponseFeature.cs
index 2a8b10ab..67cfa283 100644
--- a/src/Microsoft.AspNet.Http.Features/IHttpResponseFeature.cs
+++ b/src/Microsoft.AspNet.Http.Features/IHttpResponseFeature.cs
@@ -2,10 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
-using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNet.Http.Features
{
@@ -13,7 +11,7 @@ public interface IHttpResponseFeature
{
int StatusCode { get; set; }
string ReasonPhrase { get; set; }
- IDictionary Headers { get; set; }
+ IHeaderDictionary Headers { get; set; }
Stream Body { get; set; }
bool HasStarted { get; }
void OnStarting(Func callback, object state);
diff --git a/src/Microsoft.AspNet.Http/DefaultHttpRequest.cs b/src/Microsoft.AspNet.Http/DefaultHttpRequest.cs
index 479ead6e..b06bc6f4 100644
--- a/src/Microsoft.AspNet.Http/DefaultHttpRequest.cs
+++ b/src/Microsoft.AspNet.Http/DefaultHttpRequest.cs
@@ -123,7 +123,7 @@ public override string Protocol
public override IHeaderDictionary Headers
{
- get { return new HeaderDictionary(HttpRequestFeature.Headers); }
+ get { return HttpRequestFeature.Headers; }
}
public override IReadableStringCollection Cookies
diff --git a/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs b/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs
index e5f09f7c..81c5539a 100644
--- a/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs
+++ b/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs
@@ -43,7 +43,7 @@ public override int StatusCode
public override IHeaderDictionary Headers
{
- get { return new HeaderDictionary(HttpResponseFeature.Headers); }
+ get { return HttpResponseFeature.Headers; }
}
public override Stream Body
diff --git a/src/Microsoft.AspNet.Http/Features/HttpRequestFeature.cs b/src/Microsoft.AspNet.Http/Features/HttpRequestFeature.cs
index 900976e6..de48b71b 100644
--- a/src/Microsoft.AspNet.Http/Features/HttpRequestFeature.cs
+++ b/src/Microsoft.AspNet.Http/Features/HttpRequestFeature.cs
@@ -1,10 +1,8 @@
// 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.Collections.Generic;
using System.IO;
-using Microsoft.Extensions.Primitives;
+using Microsoft.AspNet.Http.Internal;
namespace Microsoft.AspNet.Http.Features.Internal
{
@@ -12,7 +10,7 @@ public class HttpRequestFeature : IHttpRequestFeature
{
public HttpRequestFeature()
{
- Headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ Headers = new HeaderDictionary();
Body = Stream.Null;
Protocol = string.Empty;
Scheme = string.Empty;
@@ -28,7 +26,7 @@ public HttpRequestFeature()
public string PathBase { get; set; }
public string Path { get; set; }
public string QueryString { get; set; }
- public IDictionary Headers { get; set; }
+ public IHeaderDictionary Headers { get; set; }
public Stream Body { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Http/Features/HttpResponseFeature.cs b/src/Microsoft.AspNet.Http/Features/HttpResponseFeature.cs
index 50a204ef..adb411ee 100644
--- a/src/Microsoft.AspNet.Http/Features/HttpResponseFeature.cs
+++ b/src/Microsoft.AspNet.Http/Features/HttpResponseFeature.cs
@@ -2,10 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
-using Microsoft.Extensions.Primitives;
+using Microsoft.AspNet.Http.Internal;
namespace Microsoft.AspNet.Http.Features.Internal
{
@@ -14,7 +13,7 @@ public class HttpResponseFeature : IHttpResponseFeature
public HttpResponseFeature()
{
StatusCode = 200;
- Headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ Headers = new HeaderDictionary();
Body = Stream.Null;
}
@@ -22,7 +21,7 @@ public HttpResponseFeature()
public string ReasonPhrase { get; set; }
- public IDictionary Headers { get; set; }
+ public IHeaderDictionary Headers { get; set; }
public Stream Body { get; set; }
diff --git a/src/Microsoft.AspNet.Owin/DictionaryStringArrayWrapper.cs b/src/Microsoft.AspNet.Owin/DictionaryStringArrayWrapper.cs
index 4aec5c9d..5fe22814 100644
--- a/src/Microsoft.AspNet.Owin/DictionaryStringArrayWrapper.cs
+++ b/src/Microsoft.AspNet.Owin/DictionaryStringArrayWrapper.cs
@@ -4,18 +4,19 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using Microsoft.AspNet.Http;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNet.Owin
{
internal class DictionaryStringArrayWrapper : IDictionary
{
- public DictionaryStringArrayWrapper(IDictionary inner)
+ public DictionaryStringArrayWrapper(IHeaderDictionary inner)
{
Inner = inner;
}
- public readonly IDictionary Inner;
+ public readonly IHeaderDictionary Inner;
private KeyValuePair Convert(KeyValuePair item) => new KeyValuePair(item.Key, item.Value);
@@ -27,7 +28,7 @@ public DictionaryStringArrayWrapper(IDictionary inner)
string[] IDictionary.this[string key]
{
- get { return Inner[key]; }
+ get { return ((IDictionary)Inner)[key]; }
set { Inner[key] = value; }
}
diff --git a/src/Microsoft.AspNet.Owin/DictionaryStringValuesWrapper.cs b/src/Microsoft.AspNet.Owin/DictionaryStringValuesWrapper.cs
index 51cd330d..aec1a0d9 100644
--- a/src/Microsoft.AspNet.Owin/DictionaryStringValuesWrapper.cs
+++ b/src/Microsoft.AspNet.Owin/DictionaryStringValuesWrapper.cs
@@ -4,11 +4,12 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using Microsoft.AspNet.Http;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNet.Owin
{
- internal class DictionaryStringValuesWrapper : IDictionary
+ internal class DictionaryStringValuesWrapper : IHeaderDictionary
{
public DictionaryStringValuesWrapper(IDictionary inner)
{
@@ -25,6 +26,16 @@ public DictionaryStringValuesWrapper(IDictionary inner)
private string[] Convert(StringValues item) => item;
+ StringValues IHeaderDictionary.this[string key]
+ {
+ get
+ {
+ string[] values;
+ return Inner.TryGetValue(key, out values) ? values : null;
+ }
+ set { Inner[key] = value; }
+ }
+
StringValues IDictionary.this[string key]
{
get { return Inner[key]; }
diff --git a/src/Microsoft.AspNet.Owin/OwinEnvironment.cs b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs
index c6cdd639..5e4db89d 100644
--- a/src/Microsoft.AspNet.Owin/OwinEnvironment.cs
+++ b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs
@@ -56,13 +56,13 @@ public OwinEnvironment(HttpContext context)
{ OwinConstants.RequestPath, new FeatureMap(feature => feature.Path, () => string.Empty, (feature, value) => feature.Path = Convert.ToString(value)) },
{ OwinConstants.RequestQueryString, new FeatureMap(feature => Utilities.RemoveQuestionMark(feature.QueryString), () => string.Empty,
(feature, value) => feature.QueryString = Utilities.AddQuestionMark(Convert.ToString(value))) },
- { OwinConstants.RequestHeaders, new FeatureMap(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeDictionaryStringValues((IDictionary)value)) },
+ { OwinConstants.RequestHeaders, new FeatureMap(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeHeaderDictionary((IDictionary)value)) },
{ OwinConstants.RequestBody, new FeatureMap(feature => feature.Body, () => Stream.Null, (feature, value) => feature.Body = (Stream)value) },
{ OwinConstants.RequestUser, new FeatureMap(feature => feature.User, () => null, (feature, value) => feature.User = (ClaimsPrincipal)value) },
{ OwinConstants.ResponseStatusCode, new FeatureMap(feature => feature.StatusCode, () => 200, (feature, value) => feature.StatusCode = Convert.ToInt32(value)) },
{ OwinConstants.ResponseReasonPhrase, new FeatureMap(feature => feature.ReasonPhrase, (feature, value) => feature.ReasonPhrase = Convert.ToString(value)) },
- { OwinConstants.ResponseHeaders, new FeatureMap(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeDictionaryStringValues((IDictionary)value)) },
+ { OwinConstants.ResponseHeaders, new FeatureMap(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeHeaderDictionary((IDictionary)value)) },
{ OwinConstants.ResponseBody, new FeatureMap(feature => feature.Body, () => Stream.Null, (feature, value) => feature.Body = (Stream)value) },
{ OwinConstants.CommonKeys.OnSendingHeaders, new FeatureMap(
feature => new Action, object>((cb, state) => {
diff --git a/src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs b/src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs
index 72a7b19f..6d955194 100644
--- a/src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs
+++ b/src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs
@@ -106,9 +106,9 @@ string IHttpRequestFeature.QueryString
set { Prop(OwinConstants.RequestQueryString, Utilities.RemoveQuestionMark(value)); }
}
- IDictionary IHttpRequestFeature.Headers
+ IHeaderDictionary IHttpRequestFeature.Headers
{
- get { return Utilities.MakeDictionaryStringValues(Prop>(OwinConstants.RequestHeaders)); }
+ get { return Utilities.MakeHeaderDictionary(Prop>(OwinConstants.RequestHeaders)); }
set { Prop(OwinConstants.RequestHeaders, Utilities.MakeDictionaryStringArray(value)); }
}
@@ -136,9 +136,9 @@ string IHttpResponseFeature.ReasonPhrase
set { Prop(OwinConstants.ResponseReasonPhrase, value); }
}
- IDictionary IHttpResponseFeature.Headers
+ IHeaderDictionary IHttpResponseFeature.Headers
{
- get { return Utilities.MakeDictionaryStringValues(Prop>(OwinConstants.ResponseHeaders)); }
+ get { return Utilities.MakeHeaderDictionary(Prop>(OwinConstants.ResponseHeaders)); }
set { Prop(OwinConstants.ResponseHeaders, Utilities.MakeDictionaryStringArray(value)); }
}
diff --git a/src/Microsoft.AspNet.Owin/Utilities.cs b/src/Microsoft.AspNet.Owin/Utilities.cs
index a7aa9a75..00789a86 100644
--- a/src/Microsoft.AspNet.Owin/Utilities.cs
+++ b/src/Microsoft.AspNet.Owin/Utilities.cs
@@ -46,7 +46,7 @@ internal static ClaimsPrincipal MakeClaimsPrincipal(IPrincipal principal)
return new ClaimsPrincipal(principal);
}
- internal static IDictionary MakeDictionaryStringValues(IDictionary dictionary)
+ internal static IHeaderDictionary MakeHeaderDictionary(IDictionary dictionary)
{
var wrapper = dictionary as DictionaryStringArrayWrapper;
if (wrapper != null)
@@ -56,7 +56,7 @@ internal static IDictionary MakeDictionaryStringValues(IDi
return new DictionaryStringValuesWrapper(dictionary);
}
- internal static IDictionary MakeDictionaryStringArray(IDictionary dictionary)
+ internal static IDictionary MakeDictionaryStringArray(IHeaderDictionary dictionary)
{
var wrapper = dictionary as DictionaryStringValuesWrapper;
if (wrapper != null)
diff --git a/test/Microsoft.AspNet.Http.Extensions.Tests/ResponseExtensionTests.cs b/test/Microsoft.AspNet.Http.Extensions.Tests/ResponseExtensionTests.cs
index 383f7531..efa5f370 100644
--- a/test/Microsoft.AspNet.Http.Extensions.Tests/ResponseExtensionTests.cs
+++ b/test/Microsoft.AspNet.Http.Extensions.Tests/ResponseExtensionTests.cs
@@ -44,7 +44,7 @@ private class StartedResponseFeature : IHttpResponseFeature
public bool HasStarted { get { return true; } }
- public IDictionary Headers { get; set; }
+ public IHeaderDictionary Headers { get; set; }
public string ReasonPhrase { get; set; }
diff --git a/test/Microsoft.AspNet.Http.Tests/DefaultHttpRequestTests.cs b/test/Microsoft.AspNet.Http.Tests/DefaultHttpRequestTests.cs
index 33f6a3ac..e020da61 100644
--- a/test/Microsoft.AspNet.Http.Tests/DefaultHttpRequestTests.cs
+++ b/test/Microsoft.AspNet.Http.Tests/DefaultHttpRequestTests.cs
@@ -65,7 +65,7 @@ public void Host_GetsHostFromHeaders()
// Arrange
const string expected = "localhost:9001";
- var headers = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ var headers = new HeaderDictionary()
{
{ "Host", expected },
};
@@ -85,7 +85,7 @@ public void Host_DecodesPunyCode()
// Arrange
const string expected = "löcalhöst";
- var headers = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ var headers = new HeaderDictionary()
{
{ "Host", "xn--lcalhst-90ae" },
};
@@ -105,7 +105,7 @@ public void Host_EncodesPunyCode()
// Arrange
const string expected = "xn--lcalhst-90ae";
- var headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ var headers = new HeaderDictionary();
var request = CreateRequest(headers);
@@ -188,7 +188,7 @@ public void Cookies_GetAndSet()
Assert.Equal(new[] { "name2=value2" }, cookieHeaders);
}
- private static HttpRequest CreateRequest(IDictionary headers)
+ private static HttpRequest CreateRequest(IHeaderDictionary headers)
{
var context = new DefaultHttpContext();
context.Features.Get().Headers = headers;
@@ -217,7 +217,7 @@ private static HttpRequest GetRequestWithAcceptCharsetHeader(string acceptCharse
private static HttpRequest GetRequestWithHeader(string headerName, string headerValue)
{
- var headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ var headers = new HeaderDictionary();
if (headerValue != null)
{
headers.Add(headerName, headerValue);