diff --git a/HttpAbstractions.sln b/HttpAbstractions.sln
index 872a0e3a..36488a5d 100644
--- a/HttpAbstractions.sln
+++ b/HttpAbstractions.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
-VisualStudioVersion = 14.0.22823.1
+VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5A15F1C-885A-452A-A731-B0173DDBD913}"
EndProject
@@ -43,6 +43,14 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.WebEnco
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Html.Abstractions", "src\Microsoft.AspNet.Html.Abstractions\Microsoft.AspNet.Html.Abstractions.xproj", "{68A28E4A-3ADE-4187-9625-4FF185887CB3}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{982F09D8-621E-4872-BA7B-BBDEA47D1EFD}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleApp", "samples\SampleApp\SampleApp.xproj", "{1D0764B4-1DEB-4232-A714-D4B7E846918A}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Primitives", "src\Microsoft.AspNet.Primitives\Microsoft.AspNet.Primitives.xproj", "{E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Primitives.Tests", "test\Microsoft.AspNet.Primitives.Tests\Microsoft.AspNet.Primitives.Tests.xproj", "{61F72E92-B3AE-4A10-B838-44F80AED40AE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -245,6 +253,42 @@ Global
{68A28E4A-3ADE-4187-9625-4FF185887CB3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{68A28E4A-3ADE-4187-9625-4FF185887CB3}.Release|x86.ActiveCfg = Release|Any CPU
{68A28E4A-3ADE-4187-9625-4FF185887CB3}.Release|x86.Build.0 = Release|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Debug|x86.Build.0 = Debug|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Release|x86.ActiveCfg = Release|Any CPU
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A}.Release|x86.Build.0 = Release|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Debug|x86.Build.0 = Debug|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Release|x86.ActiveCfg = Release|Any CPU
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}.Release|x86.Build.0 = Release|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Debug|x86.Build.0 = Debug|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Release|x86.ActiveCfg = Release|Any CPU
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -268,5 +312,8 @@ Global
{7AE2731D-43CD-4CF8-850A-4914DE2CE930} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21}
{BE9112CB-D87D-4080-9CC3-24492D49CBE6} = {A5A15F1C-885A-452A-A731-B0173DDBD913}
{68A28E4A-3ADE-4187-9625-4FF185887CB3} = {A5A15F1C-885A-452A-A731-B0173DDBD913}
+ {1D0764B4-1DEB-4232-A714-D4B7E846918A} = {982F09D8-621E-4872-BA7B-BBDEA47D1EFD}
+ {E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A} = {A5A15F1C-885A-452A-A731-B0173DDBD913}
+ {61F72E92-B3AE-4A10-B838-44F80AED40AE} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21}
EndGlobalSection
EndGlobal
diff --git a/samples/SampleApp/Program.cs b/samples/SampleApp/Program.cs
new file mode 100644
index 00000000..295aee93
--- /dev/null
+++ b/samples/SampleApp/Program.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Diagnostics;
+using Microsoft.AspNet.Primitives;
+
+namespace SampleApp
+{
+ public class Program
+ {
+ public void Main(string[] args)
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ Stopwatch timer = new Stopwatch();
+ timer.Start();
+ string myString;
+ string[] myArray;
+ StringValues myValues;
+ for (int j = 0; j < 100000000; j++)
+ {
+ myString = new string('a', 40);
+ myArray = new[] { myString };
+ // myValues = new StringValues(myString);
+ myValues = new StringValues(myArray);
+ }
+ timer.Stop();
+ Console.WriteLine(timer.Elapsed + ", " + Environment.WorkingSet);
+ }
+ }
+ }
+}
diff --git a/samples/SampleApp/SampleApp.xproj b/samples/SampleApp/SampleApp.xproj
new file mode 100644
index 00000000..dcc1bdf2
--- /dev/null
+++ b/samples/SampleApp/SampleApp.xproj
@@ -0,0 +1,20 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ 1d0764b4-1deb-4232-a714-d4b7e846918a
+ SampleApp
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+
+ 2.0
+
+
+
diff --git a/samples/SampleApp/project.json b/samples/SampleApp/project.json
new file mode 100644
index 00000000..b3b5f51e
--- /dev/null
+++ b/samples/SampleApp/project.json
@@ -0,0 +1,15 @@
+{
+ "version": "1.0.0-*",
+
+ "dependencies": {
+ "Microsoft.AspNet.Http": "1.0.0-*"
+ },
+
+ "commands": {
+ "SampleApp": "SampleApp"
+ },
+
+ "frameworks": {
+ "dnx451": { }
+ }
+}
diff --git a/src/Microsoft.AspNet.Http.Abstractions/IHeaderDictionary.cs b/src/Microsoft.AspNet.Http.Abstractions/IHeaderDictionary.cs
index b10101cb..56c9f49c 100644
--- a/src/Microsoft.AspNet.Http.Abstractions/IHeaderDictionary.cs
+++ b/src/Microsoft.AspNet.Http.Abstractions/IHeaderDictionary.cs
@@ -2,29 +2,30 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
+using Microsoft.AspNet.Primitives;
namespace Microsoft.AspNet.Http
{
///
/// Represents request and response headers
///
- public interface IHeaderDictionary : IReadableStringCollection, IDictionary
+ public interface IHeaderDictionary : IReadableStringCollection, IDictionary
{
+ // This property is duplicated to resolve an ambiguity between IReadableStringCollection and IDictionary
///
- /// Get or sets the associated value from the collection as a single string.
+ ///
///
- /// The header name.
- /// the associated value from the collection as a single string or null if the key is not present.
- new string this[string key] { get; set; }
+ ///
+ /// 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
+ // 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
+ // This property is duplicated to resolve an ambiguity between IReadableStringCollection.Keys and IDictionary.Keys
///
/// Gets a collection containing the keys.
///
@@ -36,21 +37,14 @@ public interface IHeaderDictionary : IReadableStringCollection, IDictionary
/// The header name.
/// the associated values from the collection separated into individual values, or null if the key is not present.
- IList GetCommaSeparatedValues(string key);
+ StringValues GetCommaSeparatedValues(string key);
///
- /// Add a new value. Appends to the header if already present
+ /// Add a new value. Appends to the header list if already present
///
/// The header name.
/// The header value.
- void Append(string key, string value);
-
- ///
- /// Add new values. Each item remains a separate array entry.
- ///
- /// The header name.
- /// The header values.
- void AppendValues(string key, params string[] values);
+ void Append(string key, StringValues value);
///
/// Quotes any values containing comas, and then coma joins all of the values with any existing values.
@@ -59,21 +53,6 @@ public interface IHeaderDictionary : IReadableStringCollection, IDictionaryThe header values.
void AppendCommaSeparatedValues(string key, params string[] values);
- ///
- /// Sets a specific header value.
- ///
- /// The header name.
- /// The header value.
- [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Set", Justification = "Re-evaluate later.")]
- void Set(string key, string value);
-
- ///
- /// Sets the specified header values without modification.
- ///
- /// The header name.
- /// The header values.
- void SetValues(string key, params string[] values);
-
///
/// Quotes any values containing comas, and then coma joins all of the values.
///
diff --git a/src/Microsoft.AspNet.Http.Abstractions/IReadableStringCollection.cs b/src/Microsoft.AspNet.Http.Abstractions/IReadableStringCollection.cs
index 589b7588..36b87e61 100644
--- a/src/Microsoft.AspNet.Http.Abstractions/IReadableStringCollection.cs
+++ b/src/Microsoft.AspNet.Http.Abstractions/IReadableStringCollection.cs
@@ -2,22 +2,22 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
+using Microsoft.AspNet.Primitives;
namespace Microsoft.AspNet.Http
{
///
/// Accessors for headers, query, forms, etc.
///
- public interface IReadableStringCollection : IEnumerable>
+ public interface IReadableStringCollection : IEnumerable>
{
///
- /// Get the associated value from the collection. Multiple values will be merged.
- /// Returns null if the key is not present.
+ /// Get the associated value from the collection.
+ /// Returns StringValues.Empty if the key is not present.
///
///
///
- string this[string key] { get; }
+ StringValues this[string key] { get; }
///
/// Gets the number of elements contained in the collection.
@@ -35,22 +35,5 @@ public interface IReadableStringCollection : IEnumerable
///
bool ContainsKey(string key);
-
- ///
- /// Get the associated value from the collection. Multiple values will be merged.
- /// Returns null if the key is not present.
- ///
- ///
- ///
- [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "Re-evaluate later.")]
- string Get(string key);
-
- ///
- /// Get the associated values from the collection in their original format.
- /// Returns null if the key is not present.
- ///
- ///
- ///
- IList GetValues(string key);
}
}
diff --git a/src/Microsoft.AspNet.Http.Abstractions/QueryString.cs b/src/Microsoft.AspNet.Http.Abstractions/QueryString.cs
index 8717638a..182dcb17 100644
--- a/src/Microsoft.AspNet.Http.Abstractions/QueryString.cs
+++ b/src/Microsoft.AspNet.Http.Abstractions/QueryString.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Text;
+using Microsoft.AspNet.Primitives;
using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders;
@@ -143,7 +144,7 @@ public static QueryString Create(IEnumerable> param
///
///
/// The resulting QueryString
- public static QueryString Create(IEnumerable> parameters)
+ public static QueryString Create(IEnumerable> parameters)
{
var builder = new StringBuilder();
bool first = true;
diff --git a/src/Microsoft.AspNet.Http.Abstractions/WebSocketManager.cs b/src/Microsoft.AspNet.Http.Abstractions/WebSocketManager.cs
index b1dd679e..06b2b043 100644
--- a/src/Microsoft.AspNet.Http.Abstractions/WebSocketManager.cs
+++ b/src/Microsoft.AspNet.Http.Abstractions/WebSocketManager.cs
@@ -1,9 +1,9 @@
// 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.Net.WebSockets;
using System.Threading.Tasks;
+using Microsoft.AspNet.Primitives;
namespace Microsoft.AspNet.Http
{
@@ -11,7 +11,7 @@ public abstract class WebSocketManager
{
public abstract bool IsWebSocketRequest { get; }
- public abstract IList WebSocketRequestedProtocols { get; }
+ public abstract StringValues WebSocketRequestedProtocols { get; }
public virtual Task AcceptWebSocketAsync()
{
diff --git a/src/Microsoft.AspNet.Http.Extensions/HeaderDictionaryTypeExtensions.cs b/src/Microsoft.AspNet.Http.Extensions/HeaderDictionaryTypeExtensions.cs
index 69f94e98..34775c9a 100644
--- a/src/Microsoft.AspNet.Http.Extensions/HeaderDictionaryTypeExtensions.cs
+++ b/src/Microsoft.AspNet.Http.Extensions/HeaderDictionaryTypeExtensions.cs
@@ -6,6 +6,7 @@
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Http.Headers;
+using Microsoft.AspNet.Primitives;
using Microsoft.Framework.Internal;
using Microsoft.Net.Http.Headers;
@@ -50,7 +51,7 @@ internal static void SetList([NotNull] this IHeaderDictionary headers, [NotNu
}
else
{
- headers.SetValues(name, values.Select(value => value.ToString()).ToArray());
+ headers[name] = values.Select(value => value.ToString()).ToArray();
}
}
@@ -98,7 +99,7 @@ internal static T Get([NotNull] this IHeaderDictionary headers, string name)
}
var value = headers[name];
- if (string.IsNullOrWhiteSpace(value))
+ if (StringValues.IsNullOrEmpty(value))
{
return default(T);
}
@@ -112,11 +113,11 @@ internal static IList GetList([NotNull] this IHeaderDictionary headers, st
if (KnownListParsers.TryGetValue(typeof(T), out temp))
{
var func = (Func, IList>)temp;
- return func(headers.GetValues(name));
+ return func(headers[name]);
}
- var values = headers.GetValues(name);
- if (values == null || !values.Any())
+ var values = headers[name];
+ if (StringValues.IsNullOrEmpty(values))
{
return null;
}
@@ -158,7 +159,7 @@ private static T GetViaReflection(string value)
return default(T);
}
- private static IList GetListViaReflection(IList values)
+ private static IList GetListViaReflection(StringValues values)
{
// TODO: Cache the reflected type for later? Only if success?
var type = typeof(T);
diff --git a/src/Microsoft.AspNet.Http.Extensions/RequestHeaders.cs b/src/Microsoft.AspNet.Http.Extensions/RequestHeaders.cs
index ac9e5425..c718ada9 100644
--- a/src/Microsoft.AspNet.Http.Extensions/RequestHeaders.cs
+++ b/src/Microsoft.AspNet.Http.Extensions/RequestHeaders.cs
@@ -285,7 +285,7 @@ public void Append([NotNull] string name, [NotNull] object value)
public void AppendList([NotNull] string name, [NotNull] IList values)
{
- Headers.AppendValues(name, values.Select(value => value.ToString()).ToArray());
+ Headers.Append(name, values.Select(value => value.ToString()).ToArray());
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Http.Extensions/ResponseHeaders.cs b/src/Microsoft.AspNet.Http.Extensions/ResponseHeaders.cs
index 8b8607a2..7ee2bb79 100644
--- a/src/Microsoft.AspNet.Http.Extensions/ResponseHeaders.cs
+++ b/src/Microsoft.AspNet.Http.Extensions/ResponseHeaders.cs
@@ -182,7 +182,7 @@ public void Append([NotNull] string name, [NotNull] object value)
public void AppendList([NotNull] string name, [NotNull] IList values)
{
- Headers.AppendValues(name, values.Select(value => value.ToString()).ToArray());
+ Headers.Append(name, values.Select(value => value.ToString()).ToArray());
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Http.Features/IHttpRequestFeature.cs b/src/Microsoft.AspNet.Http.Features/IHttpRequestFeature.cs
index c80ca29b..1bc846a7 100644
--- a/src/Microsoft.AspNet.Http.Features/IHttpRequestFeature.cs
+++ b/src/Microsoft.AspNet.Http.Features/IHttpRequestFeature.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.IO;
+using Microsoft.AspNet.Primitives;
namespace Microsoft.AspNet.Http.Features
{
@@ -14,7 +15,7 @@ public interface IHttpRequestFeature
string PathBase { get; set; }
string Path { get; set; }
string QueryString { get; set; }
- IDictionary Headers { get; set; }
+ IDictionary 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 d530718b..5f32e338 100644
--- a/src/Microsoft.AspNet.Http.Features/IHttpResponseFeature.cs
+++ b/src/Microsoft.AspNet.Http.Features/IHttpResponseFeature.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
+using Microsoft.AspNet.Primitives;
namespace Microsoft.AspNet.Http.Features
{
@@ -12,7 +13,7 @@ public interface IHttpResponseFeature
{
int StatusCode { get; set; }
string ReasonPhrase { get; set; }
- IDictionary Headers { get; set; }
+ IDictionary Headers { get; set; }
Stream Body { get; set; }
bool HasStarted { get; }
void OnStarting(Func
public class FormCollection : ReadableStringCollection, IFormCollection
{
- public FormCollection([NotNull] IDictionary store)
+ public FormCollection([NotNull] IDictionary store)
: this(store, new FormFileCollection())
{
}
- public FormCollection([NotNull] IDictionary store, [NotNull] IFormFileCollection files)
+ public FormCollection([NotNull] IDictionary store, [NotNull] IFormFileCollection files)
: base(store)
{
Files = files;
diff --git a/src/Microsoft.AspNet.Http/HeaderDictionary.cs b/src/Microsoft.AspNet.Http/HeaderDictionary.cs
index 04c2d556..62a68137 100644
--- a/src/Microsoft.AspNet.Http/HeaderDictionary.cs
+++ b/src/Microsoft.AspNet.Http/HeaderDictionary.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Http.Internal;
+using Microsoft.AspNet.Primitives;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Http.Internal
@@ -15,7 +16,7 @@ namespace Microsoft.AspNet.Http.Internal
///
public class HeaderDictionary : IHeaderDictionary
{
- public HeaderDictionary() : this(new Dictionary(StringComparer.OrdinalIgnoreCase))
+ public HeaderDictionary() : this(new Dictionary(StringComparer.OrdinalIgnoreCase))
{
}
@@ -23,12 +24,12 @@ public HeaderDictionary() : this(new Dictionary(StringComparer
/// Initializes a new instance of the class.
///
/// The underlying data store.
- public HeaderDictionary([NotNull] IDictionary store)
+ public HeaderDictionary([NotNull] IDictionary store)
{
Store = store;
}
- private IDictionary Store { get; set; }
+ private IDictionary Store { get; set; }
///
/// Gets an that contains the keys in the ;.
@@ -42,7 +43,7 @@ public ICollection Keys
///
///
///
- public ICollection Values
+ public ICollection Values
{
get { return Store.Values; }
}
@@ -69,11 +70,11 @@ public bool IsReadOnly
/// Get or sets the associated value from the collection as a single string.
///
/// The header name.
- /// the associated value from the collection as a single string or null if the key is not present.
- public string this[string key]
+ /// the associated value from the collection as a StringValues or StringValues.Empty if the key is not present.
+ public StringValues this[string key]
{
- get { return Get(key); }
- set { Set(key, value); }
+ get { return ParsingHelpers.GetHeader(Store, key); }
+ set { ParsingHelpers.SetHeader(Store, key, value); }
}
///
@@ -81,7 +82,7 @@ public string this[string key]
///
/// The header name.
///
- string[] IDictionary.this[string key]
+ StringValues IDictionary.this[string key]
{
get { return Store[key]; }
set { Store[key] = value; }
@@ -91,7 +92,7 @@ string[] IDictionary.this[string key]
/// Returns an enumerator that iterates through a collection.
///
/// An object that can be used to iterate through the collection.
- public IEnumerator> GetEnumerator()
+ IEnumerator> IEnumerable>.GetEnumerator()
{
return Store.GetEnumerator();
}
@@ -102,59 +103,29 @@ public IEnumerator> GetEnumerator()
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator()
{
- return GetEnumerator();
- }
-
- ///
- /// Get the associated value from the collection as a single string.
- ///
- /// The header name.
- /// the associated value from the collection as a single string or null if the key is not present.
- public string Get(string key)
- {
- return ParsingHelpers.GetHeader(Store, key);
+ return Store.GetEnumerator();
}
- ///
- /// Get the associated values from the collection without modification.
- ///
- /// The header name.
- /// the associated value from the collection without modification, or null if the key is not present.
- public IList GetValues(string key)
- {
- return ParsingHelpers.GetHeaderUnmodified(Store, key);
- }
///
/// Get the associated values from the collection separated into individual values.
/// Quoted values will not be split, and the quotes will be removed.
///
/// The header name.
- /// the associated values from the collection separated into individual values, or null if the key is not present.
- public IList GetCommaSeparatedValues(string key)
+ /// the associated values from the collection separated into individual values, or StringValues.Empty if the key is not present.
+ public StringValues GetCommaSeparatedValues(string key)
{
- IEnumerable values = ParsingHelpers.GetHeaderSplit(Store, key);
- return values == null ? null : values.ToList();
- }
-
- ///
- /// Add a new value. Appends to the header if already present
- ///
- /// The header name.
- /// The header value.
- public void Append(string key, string value)
- {
- ParsingHelpers.AppendHeader(Store, key, value);
+ return ParsingHelpers.GetHeaderSplit(Store, key);
}
///
/// Add new values. Each item remains a separate array entry.
///
/// The header name.
- /// The header values.
- public void AppendValues(string key, params string[] values)
+ /// The header value.
+ public void Append(string key, StringValues value)
{
- ParsingHelpers.AppendHeaderUnmodified(Store, key, values);
+ ParsingHelpers.AppendHeaderUnmodified(Store, key, value);
}
///
@@ -167,26 +138,6 @@ public void AppendCommaSeparatedValues(string key, params string[] values)
ParsingHelpers.AppendHeaderJoined(Store, key, values);
}
- ///
- /// Sets a specific header value.
- ///
- /// The header name.
- /// The header value.
- public void Set(string key, string value)
- {
- ParsingHelpers.SetHeader(Store, key, value);
- }
-
- ///
- /// Sets the specified header values without modification.
- ///
- /// The header name.
- /// The header values.
- public void SetValues(string key, params string[] values)
- {
- ParsingHelpers.SetHeaderUnmodified(Store, key, values);
- }
-
///
/// Quotes any values containing comas, and then coma joins all of the values.
///
@@ -202,7 +153,7 @@ public void SetCommaSeparatedValues(string key, params string[] values)
///
/// The header name.
/// The header values.
- public void Add(string key, string[] value)
+ public void Add(string key, StringValues value)
{
Store.Add(key, value);
}
@@ -233,7 +184,7 @@ public bool Remove(string key)
/// The header name.
/// The value.
/// true if the contains the key; otherwise, false.
- public bool TryGetValue(string key, out string[] value)
+ public bool TryGetValue(string key, out StringValues value)
{
return Store.TryGetValue(key, out value);
}
@@ -242,7 +193,7 @@ public bool TryGetValue(string key, out string[] value)
/// Adds a new list of items to the collection.
///
/// The item to add.
- public void Add(KeyValuePair item)
+ public void Add(KeyValuePair item)
{
Store.Add(item);
}
@@ -260,7 +211,7 @@ public void Clear()
///
/// The item.
/// true if the specified object occurs within this collection; otherwise, false.
- public bool Contains(KeyValuePair item)
+ public bool Contains(KeyValuePair item)
{
return Store.Contains(item);
}
@@ -270,7 +221,7 @@ public bool Contains(KeyValuePair item)
///
/// The one-dimensional Array that is the destination of the specified objects copied from the .
/// The zero-based index in at which copying begins.
- public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
{
Store.CopyTo(array, arrayIndex);
}
@@ -280,7 +231,7 @@ public void CopyTo(KeyValuePair[] array, int arrayIndex)
///
/// The item.
/// true if the specified object was removed from the collection; otherwise, false.
- public bool Remove(KeyValuePair item)
+ public bool Remove(KeyValuePair item)
{
return Store.Remove(item);
}
diff --git a/src/Microsoft.AspNet.Http/ParsingHelpers.cs b/src/Microsoft.AspNet.Http/ParsingHelpers.cs
index 45ac370e..f8a91e65 100644
--- a/src/Microsoft.AspNet.Http/ParsingHelpers.cs
+++ b/src/Microsoft.AspNet.Http/ParsingHelpers.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Microsoft.AspNet.Primitives;
using Microsoft.Framework.Internal;
using Microsoft.Net.Http.Headers;
@@ -76,9 +77,9 @@ public override int GetHashCode()
[System.CodeDom.Compiler.GeneratedCode("App_Packages", "")]
internal struct HeaderSegmentCollection : IEnumerable, IEquatable
{
- private readonly string[] _headers;
+ private readonly StringValues _headers;
- public HeaderSegmentCollection(string[] headers)
+ public HeaderSegmentCollection(StringValues headers)
{
_headers = headers;
}
@@ -102,7 +103,7 @@ public override bool Equals(object obj)
public override int GetHashCode()
{
- return (_headers != null ? _headers.GetHashCode() : 0);
+ return (!StringValues.IsNullOrEmpty(_headers) ? _headers.GetHashCode() : 0);
}
public static bool operator ==(HeaderSegmentCollection left, HeaderSegmentCollection right)
@@ -134,7 +135,7 @@ IEnumerator IEnumerable.GetEnumerator()
internal struct Enumerator : IEnumerator
{
- private readonly string[] _headers;
+ private readonly StringValues _headers;
private int _index;
private string _header;
@@ -149,11 +150,9 @@ internal struct Enumerator : IEnumerator
private Mode _mode;
- private static readonly string[] NoHeaders = new string[0];
-
- public Enumerator(string[] headers)
+ public Enumerator(StringValues headers)
{
- _headers = headers ?? NoHeaders;
+ _headers = headers;
_header = string.Empty;
_headerLength = -1;
_index = -1;
@@ -237,7 +236,7 @@ public bool MoveNext()
_trailingStart = -1;
// if that was the last string
- if (_index == _headers.Length)
+ if (_index == _headers.Count)
{
// no more move nexts
return false;
@@ -496,19 +495,19 @@ public override string ToString()
internal static class ParsingHelpers
{
- public static string GetHeader(IDictionary headers, string key)
+ public static StringValues GetHeader(IDictionary headers, string key)
{
- string[] values = GetHeaderUnmodified(headers, key);
- return values == null ? null : string.Join(",", values);
+ StringValues value;
+ return headers.TryGetValue(key, out value) ? value : StringValues.Empty;
}
- public static IEnumerable GetHeaderSplit(IDictionary headers, string key)
+ public static StringValues GetHeaderSplit(IDictionary headers, string key)
{
- string[] values = GetHeaderUnmodified(headers, key);
- return values == null ? null : GetHeaderSplitImplementation(values);
+ var values = GetHeaderUnmodified(headers, key);
+ return new StringValues(GetHeaderSplitImplementation(values).ToArray());
}
- private static IEnumerable GetHeaderSplitImplementation(string[] values)
+ private static IEnumerable GetHeaderSplitImplementation(StringValues values)
{
foreach (var segment in new HeaderSegmentCollection(values))
{
@@ -519,41 +518,41 @@ private static IEnumerable GetHeaderSplitImplementation(string[] values)
}
}
- public static string[] GetHeaderUnmodified([NotNull] IDictionary headers, string key)
+ public static StringValues GetHeaderUnmodified([NotNull] IDictionary headers, string key)
{
- string[] values;
- return headers.TryGetValue(key, out values) ? values : null;
+ StringValues values;
+ return headers.TryGetValue(key, out values) ? values : StringValues.Empty;
}
- public static void SetHeader([NotNull] IDictionary headers, [NotNull] string key, string value)
+ public static void SetHeader([NotNull] IDictionary headers, [NotNull] string key, StringValues value)
{
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentNullException(nameof(key));
}
- if (string.IsNullOrWhiteSpace(value))
+ if (StringValues.IsNullOrEmpty(value))
{
headers.Remove(key);
}
else
{
- headers[key] = new[] { value };
+ headers[key] = value;
}
}
- public static void SetHeaderJoined([NotNull] IDictionary headers, [NotNull] string key, params string[] values)
+ public static void SetHeaderJoined([NotNull] IDictionary headers, [NotNull] string key, StringValues value)
{
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentNullException(nameof(key));
}
- if (values == null || values.Length == 0)
+ if (StringValues.IsNullOrEmpty(value))
{
headers.Remove(key);
}
else
{
- headers[key] = new[] { string.Join(",", values.Select(value => QuoteIfNeeded(value))) };
+ headers[key] = string.Join(",", value.Select(QuoteIfNeeded));
}
}
@@ -589,46 +588,23 @@ private static string DeQuote(string value)
return value;
}
- public static void SetHeaderUnmodified([NotNull] IDictionary headers, [NotNull] string key, params string[] values)
+ public static void SetHeaderUnmodified([NotNull] IDictionary headers, [NotNull] string key, StringValues? values)
{
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentNullException(nameof(key));
}
- if (values == null || values.Length == 0)
+ if (!values.HasValue || StringValues.IsNullOrEmpty(values.Value))
{
headers.Remove(key);
}
else
{
- headers[key] = values;
- }
- }
-
- public static void SetHeaderUnmodified([NotNull] IDictionary headers, [NotNull] string key, [NotNull] IEnumerable values)
- {
- headers[key] = values.ToArray();
- }
-
- public static void AppendHeader([NotNull] IDictionary headers, [NotNull] string key, string values)
- {
- if (string.IsNullOrWhiteSpace(values))
- {
- return;
- }
-
- string existing = GetHeader(headers, key);
- if (existing == null)
- {
- SetHeader(headers, key, values);
- }
- else
- {
- headers[key] = new[] { existing + "," + values };
+ headers[key] = values.Value;
}
}
- public static void AppendHeaderJoined([NotNull] IDictionary headers, [NotNull] string key, params string[] values)
+ public static void AppendHeaderJoined([NotNull] IDictionary headers, [NotNull] string key, params string[] values)
{
if (values == null || values.Length == 0)
{
@@ -642,35 +618,29 @@ public static void AppendHeaderJoined([NotNull] IDictionary he
}
else
{
- headers[key] = new[] { existing + "," + string.Join(",", values.Select(value => QuoteIfNeeded(value))) };
+ headers[key] = existing + "," + string.Join(",", values.Select(value => QuoteIfNeeded(value)));
}
}
- public static void AppendHeaderUnmodified([NotNull] IDictionary headers, [NotNull] string key, params string[] values)
+ public static void AppendHeaderUnmodified([NotNull] IDictionary headers, [NotNull] string key, StringValues values)
{
- if (values == null || values.Length == 0)
+ if (values.Count == 0)
{
return;
}
- string[] existing = GetHeaderUnmodified(headers, key);
- if (existing == null)
- {
- SetHeaderUnmodified(headers, key, values);
- }
- else
- {
- SetHeaderUnmodified(headers, key, existing.Concat(values));
- }
+ var existing = GetHeaderUnmodified(headers, key);
+ SetHeaderUnmodified(headers, key, StringValues.Concat(existing, values));
}
public static long? GetContentLength([NotNull] IHeaderDictionary headers)
{
const NumberStyles styles = NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite;
long value;
- string rawValue = headers.Get(HeaderNames.ContentLength);
- if (!string.IsNullOrWhiteSpace(rawValue) &&
- long.TryParse(rawValue, styles, CultureInfo.InvariantCulture, out value))
+ var rawValue = headers[HeaderNames.ContentLength];
+ if (rawValue.Count == 1 &&
+ !string.IsNullOrWhiteSpace(rawValue[0]) &&
+ long.TryParse(rawValue[0], styles, CultureInfo.InvariantCulture, out value))
{
return value;
}
diff --git a/src/Microsoft.AspNet.Http/ReadableStringCollection.cs b/src/Microsoft.AspNet.Http/ReadableStringCollection.cs
index 7a07ea60..d6c4ce1f 100644
--- a/src/Microsoft.AspNet.Http/ReadableStringCollection.cs
+++ b/src/Microsoft.AspNet.Http/ReadableStringCollection.cs
@@ -3,6 +3,7 @@
using System.Collections;
using System.Collections.Generic;
+using Microsoft.AspNet.Primitives;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Http.Internal
@@ -12,16 +13,18 @@ namespace Microsoft.AspNet.Http.Internal
///
public class ReadableStringCollection : IReadableStringCollection
{
+ public static readonly IReadableStringCollection Empty = new ReadableStringCollection(new Dictionary(0));
+
///
/// Create a new wrapper
///
///
- public ReadableStringCollection([NotNull] IDictionary store)
+ public ReadableStringCollection([NotNull] IDictionary store)
{
Store = store;
}
- private IDictionary Store { get; set; }
+ private IDictionary Store { get; set; }
///
/// Gets the number of elements contained in the collection.
@@ -42,13 +45,21 @@ public ICollection Keys
///
/// Get the associated value from the collection. Multiple values will be merged.
- /// Returns null if the key is not present.
+ /// Returns StringValues.Empty if the key is not present.
///
///
///
- public string this[string key]
+ public StringValues this[string key]
{
- get { return Get(key); }
+ get
+ {
+ StringValues value;
+ if (Store.TryGetValue(key, out value))
+ {
+ return value;
+ }
+ return StringValues.Empty;
+ }
}
///
@@ -61,35 +72,12 @@ public bool ContainsKey(string key)
return Store.ContainsKey(key);
}
- ///
- /// Get the associated value from the collection. Multiple values will be merged.
- /// Returns null if the key is not present.
- ///
- ///
- ///
- public string Get(string key)
- {
- return GetJoinedValue(Store, key);
- }
-
- ///
- /// Get the associated values from the collection in their original format.
- /// Returns null if the key is not present.
- ///
- ///
- ///
- public IList GetValues(string key)
- {
- string[] values;
- Store.TryGetValue(key, out values);
- return values;
- }
///
///
///
///
- public IEnumerator> GetEnumerator()
+ public IEnumerator> GetEnumerator()
{
return Store.GetEnumerator();
}
@@ -102,15 +90,5 @@ IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
-
- private static string GetJoinedValue(IDictionary store, string key)
- {
- string[] values;
- if (store.TryGetValue(key, out values))
- {
- return string.Join(",", values);
- }
- return null;
- }
}
}
diff --git a/src/Microsoft.AspNet.Http/RequestCookiesCollection.cs b/src/Microsoft.AspNet.Http/RequestCookiesCollection.cs
index 1dc603b5..390151b4 100644
--- a/src/Microsoft.AspNet.Http/RequestCookiesCollection.cs
+++ b/src/Microsoft.AspNet.Http/RequestCookiesCollection.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using Microsoft.AspNet.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Internal
@@ -17,7 +18,7 @@ public RequestCookiesCollection()
_dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
}
- public string this[string key]
+ public StringValues this[string key]
{
get { return Get(key); }
}
@@ -88,11 +89,11 @@ public void Reparse(IList values)
}
}
- public IEnumerator> GetEnumerator()
+ public IEnumerator> GetEnumerator()
{
foreach (var pair in _dictionary)
{
- yield return new KeyValuePair(pair.Key, new[] { pair.Value });
+ yield return new KeyValuePair(pair.Key, pair.Value);
}
}
diff --git a/src/Microsoft.AspNet.Http/ResponseCookies.cs b/src/Microsoft.AspNet.Http/ResponseCookies.cs
index b47b48d5..e83d0503 100644
--- a/src/Microsoft.AspNet.Http/ResponseCookies.cs
+++ b/src/Microsoft.AspNet.Http/ResponseCookies.cs
@@ -2,8 +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.Linq;
+using Microsoft.AspNet.Primitives;
using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders;
using Microsoft.Net.Http.Headers;
@@ -33,11 +33,14 @@ public ResponseCookies([NotNull] IHeaderDictionary headers)
///
public void Append(string key, string value)
{
- Headers.AppendValues(HeaderNames.SetCookie,
- new SetCookieHeaderValue(
+ var setCookieHeaderValue = new SetCookieHeaderValue(
UrlEncoder.Default.UrlEncode(key),
UrlEncoder.Default.UrlEncode(value))
- { Path = "/" }.ToString());
+ {
+ Path = "/"
+ };
+
+ Headers[HeaderNames.SetCookie] = StringValues.Concat(Headers[HeaderNames.SetCookie], setCookieHeaderValue.ToString());
}
///
@@ -48,17 +51,18 @@ public void Append(string key, string value)
///
public void Append(string key, string value, [NotNull] CookieOptions options)
{
- Headers.AppendValues(HeaderNames.SetCookie,
- new SetCookieHeaderValue(
+ var setCookieHeaderValue = new SetCookieHeaderValue(
UrlEncoder.Default.UrlEncode(key),
UrlEncoder.Default.UrlEncode(value))
- {
- Domain = options.Domain,
- Path = options.Path,
- Expires = options.Expires,
- Secure = options.Secure,
- HttpOnly = options.HttpOnly,
- }.ToString());
+ {
+ Domain = options.Domain,
+ Path = options.Path,
+ Expires = options.Expires,
+ Secure = options.Secure,
+ HttpOnly = options.HttpOnly,
+ };
+
+ Headers[HeaderNames.SetCookie] = StringValues.Concat(Headers[HeaderNames.SetCookie], setCookieHeaderValue.ToString());
}
///
@@ -70,15 +74,15 @@ public void Delete(string key)
var encodedKeyPlusEquals = UrlEncoder.Default.UrlEncode(key) + "=";
Func predicate = value => value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
- var deleteCookies = new[] { encodedKeyPlusEquals + "; expires=Thu, 01-Jan-1970 00:00:00 GMT" };
- IList existingValues = Headers.GetValues(HeaderNames.SetCookie);
- if (existingValues == null || existingValues.Count == 0)
+ StringValues deleteCookies = encodedKeyPlusEquals + "; expires=Thu, 01-Jan-1970 00:00:00 GMT";
+ var existingValues = Headers[HeaderNames.SetCookie];
+ if (StringValues.IsNullOrEmpty(existingValues))
{
- Headers.SetValues(HeaderNames.SetCookie, deleteCookies);
+ Headers[HeaderNames.SetCookie] = deleteCookies;
}
else
{
- Headers.SetValues(HeaderNames.SetCookie, existingValues.Where(value => !predicate(value)).Concat(deleteCookies).ToArray());
+ Headers[HeaderNames.SetCookie] = existingValues.Where(value => !predicate(value)).Concat(deleteCookies).ToArray();
}
}
@@ -111,10 +115,10 @@ public void Delete(string key, [NotNull] CookieOptions options)
rejectPredicate = value => value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
}
- IList existingValues = Headers.GetValues(HeaderNames.SetCookie);
- if (existingValues != null)
+ var existingValues = Headers[HeaderNames.SetCookie];
+ if (!StringValues.IsNullOrEmpty(existingValues))
{
- Headers.SetValues(HeaderNames.SetCookie, existingValues.Where(value => !rejectPredicate(value)).ToArray());
+ Headers[HeaderNames.SetCookie] = existingValues.Where(value => !rejectPredicate(value)).ToArray();
}
Append(key, string.Empty, new CookieOptions
diff --git a/src/Microsoft.AspNet.Owin/DictionaryStringArrayWrapper.cs b/src/Microsoft.AspNet.Owin/DictionaryStringArrayWrapper.cs
new file mode 100644
index 00000000..ebef5bee
--- /dev/null
+++ b/src/Microsoft.AspNet.Owin/DictionaryStringArrayWrapper.cs
@@ -0,0 +1,80 @@
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNet.Primitives;
+
+namespace Microsoft.AspNet.Owin
+{
+ internal class DictionaryStringArrayWrapper : IDictionary
+ {
+ public DictionaryStringArrayWrapper(IDictionary inner)
+ {
+ Inner = inner;
+ }
+
+ public readonly IDictionary Inner;
+
+ private KeyValuePair Convert(KeyValuePair item) => new KeyValuePair(item.Key, item.Value);
+
+ private KeyValuePair Convert(KeyValuePair item) => new KeyValuePair(item.Key, item.Value);
+
+ private StringValues Convert(string[] item) => item;
+
+ private string[] Convert(StringValues item) => item;
+
+ string[] IDictionary.this[string key]
+ {
+ get { return Inner[key]; }
+ set { Inner[key] = value; }
+ }
+
+ int ICollection>.Count => Inner.Count;
+
+ bool ICollection>.IsReadOnly => Inner.IsReadOnly;
+
+ ICollection IDictionary.Keys => Inner.Keys;
+
+ ICollection IDictionary.Values => Inner.Values.Select(Convert).ToList();
+
+ void ICollection>.Add(KeyValuePair item) => Inner.Add(Convert(item));
+
+ void IDictionary.Add(string key, string[] value) => Inner.Add(key, value);
+
+ void ICollection>.Clear() => Inner.Clear();
+
+ bool ICollection>.Contains(KeyValuePair item) => Inner.Contains(Convert(item));
+
+ bool IDictionary.ContainsKey(string key) => Inner.ContainsKey(key);
+
+ void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ foreach(var kv in Inner)
+ {
+ array[arrayIndex++] = Convert(kv);
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
+
+ IEnumerator> IEnumerable>.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
+
+ bool ICollection>.Remove(KeyValuePair item) => Inner.Remove(Convert(item));
+
+ bool IDictionary.Remove(string key) => Inner.Remove(key);
+
+ bool IDictionary.TryGetValue(string key, out string[] value)
+ {
+ StringValues temp;
+ if (Inner.TryGetValue(key, out temp))
+ {
+ value = temp;
+ return true;
+ }
+ value = default(StringValues);
+ return false;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Owin/DictionaryStringValuesWrapper.cs b/src/Microsoft.AspNet.Owin/DictionaryStringValuesWrapper.cs
new file mode 100644
index 00000000..5b7d7659
--- /dev/null
+++ b/src/Microsoft.AspNet.Owin/DictionaryStringValuesWrapper.cs
@@ -0,0 +1,80 @@
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNet.Primitives;
+
+namespace Microsoft.AspNet.Owin
+{
+ internal class DictionaryStringValuesWrapper : IDictionary
+ {
+ public DictionaryStringValuesWrapper(IDictionary inner)
+ {
+ Inner = inner;
+ }
+
+ public readonly IDictionary Inner;
+
+ private KeyValuePair Convert(KeyValuePair item) => new KeyValuePair(item.Key, item.Value);
+
+ private KeyValuePair Convert(KeyValuePair item) => new KeyValuePair(item.Key, item.Value);
+
+ private StringValues Convert(string[] item) => item;
+
+ private string[] Convert(StringValues item) => item;
+
+ StringValues IDictionary.this[string key]
+ {
+ get { return Inner[key]; }
+ set { Inner[key] = value; }
+ }
+
+ int ICollection>.Count => Inner.Count;
+
+ bool ICollection>.IsReadOnly => Inner.IsReadOnly;
+
+ ICollection IDictionary.Keys => Inner.Keys;
+
+ ICollection IDictionary.Values => Inner.Values.Select(Convert).ToList();
+
+ void ICollection>.Add(KeyValuePair item) => Inner.Add(Convert(item));
+
+ void IDictionary.Add(string key, StringValues value) => Inner.Add(key, value);
+
+ void ICollection>.Clear() => Inner.Clear();
+
+ bool ICollection>.Contains(KeyValuePair item) => Inner.Contains(Convert(item));
+
+ bool IDictionary.ContainsKey(string key) => Inner.ContainsKey(key);
+
+ void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ foreach (var kv in Inner)
+ {
+ array[arrayIndex++] = Convert(kv);
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
+
+ IEnumerator> IEnumerable>.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
+
+ bool ICollection>.Remove(KeyValuePair item) => Inner.Remove(Convert(item));
+
+ bool IDictionary.Remove(string key) => Inner.Remove(key);
+
+ bool IDictionary.TryGetValue(string key, out StringValues value)
+ {
+ string[] temp;
+ if (Inner.TryGetValue(key, out temp))
+ {
+ value = temp;
+ return true;
+ }
+ value = default(StringValues);
+ return false;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Owin/OwinEnvironment.cs b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs
index 5c231eb0..840da1b6 100644
--- a/src/Microsoft.AspNet.Owin/OwinEnvironment.cs
+++ b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs
@@ -55,12 +55,12 @@ 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 => feature.Headers, (feature, value) => feature.Headers = (IDictionary)value) },
+ { OwinConstants.RequestHeaders, new FeatureMap(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeDictionaryStringValues((IDictionary)value)) },
{ OwinConstants.RequestBody, new FeatureMap(feature => feature.Body, () => Stream.Null, (feature, value) => feature.Body = (Stream)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 => feature.Headers, (feature, value) => feature.Headers = (IDictionary)value) },
+ { OwinConstants.ResponseHeaders, new FeatureMap(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeDictionaryStringValues((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 6dd44e52..460faada 100644
--- a/src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs
+++ b/src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs
@@ -14,8 +14,10 @@
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Authentication;
+using Microsoft.AspNet.Primitives;
namespace Microsoft.AspNet.Owin
{
@@ -104,10 +106,10 @@ string IHttpRequestFeature.QueryString
set { Prop(OwinConstants.RequestQueryString, Utilities.RemoveQuestionMark(value)); }
}
- IDictionary IHttpRequestFeature.Headers
+ IDictionary IHttpRequestFeature.Headers
{
- get { return Prop>(OwinConstants.RequestHeaders); }
- set { Prop(OwinConstants.RequestHeaders, value); }
+ get { return Utilities.MakeDictionaryStringValues(Prop>(OwinConstants.RequestHeaders)); }
+ set { Prop(OwinConstants.RequestHeaders, Utilities.MakeDictionaryStringArray(value)); }
}
string IHttpRequestIdentifierFeature.TraceIdentifier
@@ -134,10 +136,10 @@ string IHttpResponseFeature.ReasonPhrase
set { Prop(OwinConstants.ResponseReasonPhrase, value); }
}
- IDictionary IHttpResponseFeature.Headers
+ IDictionary IHttpResponseFeature.Headers
{
- get { return Prop>(OwinConstants.ResponseHeaders); }
- set { Prop(OwinConstants.ResponseHeaders, value); }
+ get { return Utilities.MakeDictionaryStringValues(Prop>(OwinConstants.ResponseHeaders)); }
+ set { Prop(OwinConstants.ResponseHeaders, Utilities.MakeDictionaryStringArray(value)); }
}
Stream IHttpResponseFeature.Body
diff --git a/src/Microsoft.AspNet.Owin/Utilities.cs b/src/Microsoft.AspNet.Owin/Utilities.cs
index fd3c6903..8960cb5e 100644
--- a/src/Microsoft.AspNet.Owin/Utilities.cs
+++ b/src/Microsoft.AspNet.Owin/Utilities.cs
@@ -1,8 +1,12 @@
// 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.Security.Claims;
using System.Security.Principal;
+using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Primitives;
namespace Microsoft.AspNet.Owin
{
@@ -41,5 +45,25 @@ internal static ClaimsPrincipal MakeClaimsPrincipal(IPrincipal principal)
}
return new ClaimsPrincipal(principal);
}
+
+ internal static IDictionary MakeDictionaryStringValues(IDictionary dictionary)
+ {
+ var wrapper = dictionary as DictionaryStringArrayWrapper;
+ if (wrapper != null)
+ {
+ return wrapper.Inner;
+ }
+ return new DictionaryStringValuesWrapper(dictionary);
+ }
+
+ internal static IDictionary MakeDictionaryStringArray(IDictionary dictionary)
+ {
+ var wrapper = dictionary as DictionaryStringValuesWrapper;
+ if (wrapper != null)
+ {
+ return wrapper.Inner;
+ }
+ return new DictionaryStringArrayWrapper(dictionary);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.AspNet.Primitives/Microsoft.AspNet.Primitives.xproj b/src/Microsoft.AspNet.Primitives/Microsoft.AspNet.Primitives.xproj
new file mode 100644
index 00000000..f7b40aaa
--- /dev/null
+++ b/src/Microsoft.AspNet.Primitives/Microsoft.AspNet.Primitives.xproj
@@ -0,0 +1,20 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ e5faccd4-6327-43aa-80a9-ae6f4a3bfe6a
+ Microsoft.AspNet.Primitives
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+
+ 2.0
+
+
+
diff --git a/src/Microsoft.AspNet.Primitives/StringValues.cs b/src/Microsoft.AspNet.Primitives/StringValues.cs
new file mode 100644
index 00000000..3312808c
--- /dev/null
+++ b/src/Microsoft.AspNet.Primitives/StringValues.cs
@@ -0,0 +1,223 @@
+// 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;
+using System.Collections.Generic;
+
+namespace Microsoft.AspNet.Primitives
+{
+ ///
+ /// Represents zero/null, one, or many strings in an efficient way.
+ ///
+ public struct StringValues : IList
+ {
+ private static readonly string[] EmptyArray = new string[0];
+ public static readonly StringValues Empty = new StringValues(EmptyArray);
+
+ private readonly string _value;
+ private readonly string[] _values;
+
+ public StringValues(string value)
+ {
+ _value = value;
+ _values = null;
+ }
+
+ public StringValues(string[] values)
+ {
+ _value = null;
+ _values = values;
+ }
+
+ public static implicit operator StringValues(string value)
+ {
+ return new StringValues(value);
+ }
+
+ public static implicit operator StringValues(string[] values)
+ {
+ return new StringValues(values);
+ }
+
+ public static implicit operator string (StringValues values)
+ {
+ return values.GetStringValue();
+ }
+
+ public static implicit operator string[] (StringValues value)
+ {
+ if (value._values != null)
+ {
+ return value._values;
+ }
+ if (value._value != null)
+ {
+ return new string[1] { value._value };
+ }
+ return EmptyArray;
+ }
+
+ public int Count => _values?.Length ?? (_value != null ? 1 : 0);
+
+ bool ICollection.IsReadOnly
+ {
+ get { return true; }
+ }
+
+ string IList.this[int index]
+ {
+ get { return this[index]; }
+ set { throw new NotSupportedException(); }
+ }
+
+ public string this[int key]
+ {
+ get
+ {
+ if (_values != null)
+ {
+ return _values[key]; // may throw
+ }
+ if (key == 0 && _value != null)
+ {
+ return _value;
+ }
+ return EmptyArray[0]; // throws
+ }
+ }
+
+ public override string ToString()
+ {
+ return GetStringValue() ?? string.Empty;
+ }
+
+ private string GetStringValue()
+ {
+ if (_values == null)
+ {
+ return _value;
+ }
+ switch (_values.Length)
+ {
+ case 0: return null;
+ case 1: return _values[0];
+ default: return string.Join(",", _values);
+ }
+ }
+
+ int IList.IndexOf(string item)
+ {
+ var index = 0;
+ foreach (var value in this)
+ {
+ if (string.Equals(value, item, StringComparison.Ordinal))
+ {
+ return index;
+ }
+ index += 1;
+ }
+ return -1;
+ }
+
+ bool ICollection.Contains(string item)
+ {
+ return ((IList)this).IndexOf(item) >= 0;
+ }
+
+ void ICollection.CopyTo(string[] array, int arrayIndex)
+ {
+ for(int i = 0; i < Count; i++)
+ {
+ array[arrayIndex + i] = this[i];
+ }
+ }
+
+ void ICollection.Add(string item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.Insert(int index, string item)
+ {
+ throw new NotSupportedException();
+ }
+
+ bool ICollection.Remove(string item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.RemoveAt(int index)
+ {
+ throw new NotSupportedException();
+ }
+
+ void ICollection.Clear()
+ {
+ throw new NotSupportedException();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable)this).GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ if (_values == null)
+ {
+ yield return _value;
+ }
+ else
+ {
+ for (int i = 0; i < _values.Length; i++)
+ {
+ yield return _values[i];
+ }
+ }
+ }
+
+ public static bool IsNullOrEmpty(StringValues value)
+ {
+ if (value._values == null)
+ {
+ return string.IsNullOrEmpty(value._value);
+ }
+ switch (value._values.Length)
+ {
+ case 0: return true;
+ case 1: return string.IsNullOrEmpty(value._values[0]);
+ default: return false;
+ }
+ }
+
+ public static StringValues Concat(StringValues values1, StringValues values2)
+ {
+ var count1 = values1.Count;
+ var count2 = values2.Count;
+
+ if (count1 == 0)
+ {
+ return values2;
+ }
+
+ if (count2 == 0)
+ {
+ return values1;
+ }
+
+ var combined = new string[count1 + count2];
+ var index = 0;
+ foreach (var value in values1)
+ {
+ combined[index++] = value;
+ }
+ foreach (var value in values2)
+ {
+ combined[index++] = value;
+ }
+ return new StringValues(combined);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Primitives/project.json b/src/Microsoft.AspNet.Primitives/project.json
new file mode 100644
index 00000000..4ed4ca97
--- /dev/null
+++ b/src/Microsoft.AspNet.Primitives/project.json
@@ -0,0 +1,23 @@
+{
+ "version": "1.0.0-*",
+ "description": "ASP.NET 5 primitive definitions.",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/aspnet/httpabstractions"
+ },
+ "dependencies": {
+ "Microsoft.Framework.NotNullAttribute.Sources": {
+ "type": "build",
+ "version": "1.0.0-*"
+ }
+ },
+ "frameworks": {
+ "net451": { },
+ "dnx451": { },
+ "dnxcore50": {
+ "dependencies": {
+ "System.Collections": "4.0.11-beta-*"
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.WebUtilities/FormReader.cs b/src/Microsoft.AspNet.WebUtilities/FormReader.cs
index 0f990043..707fe702 100644
--- a/src/Microsoft.AspNet.WebUtilities/FormReader.cs
+++ b/src/Microsoft.AspNet.WebUtilities/FormReader.cs
@@ -7,6 +7,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNet.Primitives;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.WebUtilities
@@ -150,11 +151,11 @@ private async Task BufferAsync(CancellationToken cancellationToken)
///
/// The HTTP form body to parse.
/// The collection containing the parsed HTTP form body.
- public static IDictionary ReadForm(string text)
+ public static IDictionary ReadForm(string text)
{
var reader = new FormReader(text);
- var accumulator = new KeyValueAccumulator(StringComparer.OrdinalIgnoreCase);
+ var accumulator = new KeyValueAccumulator();
var pair = reader.ReadNextPair();
while (pair.HasValue)
{
@@ -170,7 +171,7 @@ public static IDictionary ReadForm(string text)
///
/// The HTTP form body to parse.
/// The collection containing the parsed HTTP form body.
- public static Task> ReadFormAsync(Stream stream, CancellationToken cancellationToken = new CancellationToken())
+ public static Task> ReadFormAsync(Stream stream, CancellationToken cancellationToken = new CancellationToken())
{
return ReadFormAsync(stream, Encoding.UTF8, cancellationToken);
}
@@ -180,11 +181,11 @@ public static IDictionary ReadForm(string text)
///
/// The HTTP form body to parse.
/// The collection containing the parsed HTTP form body.
- public static async Task> ReadFormAsync(Stream stream, Encoding encoding, CancellationToken cancellationToken = new CancellationToken())
+ public static async Task> ReadFormAsync(Stream stream, Encoding encoding, CancellationToken cancellationToken = new CancellationToken())
{
var reader = new FormReader(stream, encoding);
- var accumulator = new KeyValueAccumulator(StringComparer.OrdinalIgnoreCase);
+ var accumulator = new KeyValueAccumulator();
var pair = await reader.ReadNextPairAsync(cancellationToken);
while (pair.HasValue)
{
diff --git a/src/Microsoft.AspNet.WebUtilities/KeyValueAccumulator.cs b/src/Microsoft.AspNet.WebUtilities/KeyValueAccumulator.cs
index 9fb4b2b6..baf04ca2 100644
--- a/src/Microsoft.AspNet.WebUtilities/KeyValueAccumulator.cs
+++ b/src/Microsoft.AspNet.WebUtilities/KeyValueAccumulator.cs
@@ -1,38 +1,37 @@
// 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 Microsoft.Framework.Internal;
+using Microsoft.AspNet.Primitives;
namespace Microsoft.AspNet.WebUtilities
{
- public class KeyValueAccumulator
+ public class KeyValueAccumulator
{
- private Dictionary> _accumulator;
- IEqualityComparer _comparer;
+ private Dictionary> _accumulator;
- public KeyValueAccumulator([NotNull] IEqualityComparer comparer)
+ public KeyValueAccumulator()
{
- _comparer = comparer;
- _accumulator = new Dictionary>(comparer);
+ _accumulator = new Dictionary>(StringComparer.OrdinalIgnoreCase);
}
- public void Append(TKey key, TValue value)
+ public void Append(string key, string value)
{
- List values;
+ List values;
if (_accumulator.TryGetValue(key, out values))
{
values.Add(value);
}
else
{
- _accumulator[key] = new List(1) { value };
+ _accumulator[key] = new List(1) { value };
}
}
- public IDictionary GetResults()
+ public IDictionary GetResults()
{
- var results = new Dictionary(_comparer);
+ var results = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (var kv in _accumulator)
{
results.Add(kv.Key, kv.Value.ToArray());
diff --git a/src/Microsoft.AspNet.WebUtilities/MultipartReader.cs b/src/Microsoft.AspNet.WebUtilities/MultipartReader.cs
index 244117e3..04990ef8 100644
--- a/src/Microsoft.AspNet.WebUtilities/MultipartReader.cs
+++ b/src/Microsoft.AspNet.WebUtilities/MultipartReader.cs
@@ -7,6 +7,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNet.Primitives;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.WebUtilities
@@ -64,10 +65,10 @@ public MultipartReader([NotNull] string boundary, [NotNull] Stream stream, int b
return new MultipartSection() { Headers = headers, Body = _currentStream, BaseStreamOffset = baseStreamOffset };
}
- private async Task> ReadHeadersAsync(CancellationToken cancellationToken)
+ private async Task> ReadHeadersAsync(CancellationToken cancellationToken)
{
int totalSize = 0;
- var accumulator = new KeyValueAccumulator(StringComparer.OrdinalIgnoreCase);
+ var accumulator = new KeyValueAccumulator();
var line = await _stream.ReadLineAsync(HeaderLengthLimit, cancellationToken);
while (!string.IsNullOrEmpty(line))
{
diff --git a/src/Microsoft.AspNet.WebUtilities/MultipartSection.cs b/src/Microsoft.AspNet.WebUtilities/MultipartSection.cs
index 5618b826..e0a35dee 100644
--- a/src/Microsoft.AspNet.WebUtilities/MultipartSection.cs
+++ b/src/Microsoft.AspNet.WebUtilities/MultipartSection.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.IO;
+using Microsoft.AspNet.Primitives;
namespace Microsoft.AspNet.WebUtilities
{
@@ -12,10 +13,10 @@ public string ContentType
{
get
{
- string[] values;
+ StringValues values;
if (Headers.TryGetValue("Content-Type", out values))
{
- return string.Join(", ", values);
+ return values;
}
return null;
}
@@ -25,16 +26,16 @@ public string ContentDisposition
{
get
{
- string[] values;
+ StringValues values;
if (Headers.TryGetValue("Content-Disposition", out values))
{
- return string.Join(", ", values);
+ return values;
}
return null;
}
}
- public IDictionary Headers { get; set; }
+ public IDictionary Headers { get; set; }
public Stream Body { get; set; }
diff --git a/src/Microsoft.AspNet.WebUtilities/QueryHelpers.cs b/src/Microsoft.AspNet.WebUtilities/QueryHelpers.cs
index 6df0e2d7..d95d11e1 100644
--- a/src/Microsoft.AspNet.WebUtilities/QueryHelpers.cs
+++ b/src/Microsoft.AspNet.WebUtilities/QueryHelpers.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Text;
+using Microsoft.AspNet.Primitives;
using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders;
@@ -72,13 +73,13 @@ private static string AddQueryString(
///
/// The raw query string value, with or without the leading '?'.
/// A collection of parsed keys and values.
- public static IDictionary ParseQuery(string queryString)
+ public static IDictionary ParseQuery(string queryString)
{
if (!string.IsNullOrEmpty(queryString) && queryString[0] == '?')
{
queryString = queryString.Substring(1);
}
- var accumulator = new KeyValueAccumulator(StringComparer.OrdinalIgnoreCase);
+ var accumulator = new KeyValueAccumulator();
int textLength = queryString.Length;
int equalIndex = queryString.IndexOf('=');
diff --git a/src/Microsoft.AspNet.WebUtilities/project.json b/src/Microsoft.AspNet.WebUtilities/project.json
index 496449f4..6796a73b 100644
--- a/src/Microsoft.AspNet.WebUtilities/project.json
+++ b/src/Microsoft.AspNet.WebUtilities/project.json
@@ -6,6 +6,7 @@
"url": "git://github.com/aspnet/httpabstractions"
},
"dependencies": {
+ "Microsoft.AspNet.Primitives": "1.0.0-*",
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" },
"Microsoft.Framework.WebEncoders.Core": "1.0.0-*"
},
@@ -13,7 +14,6 @@
"dnx451": { },
"dnxcore50": {
"dependencies": {
- "System.Collections": "4.0.11-beta-*",
"System.Diagnostics.Debug": "4.0.11-beta-*",
"System.IO": "4.0.11-beta-*",
"System.IO.FileSystem": "4.0.1-beta-*",
diff --git a/test/Microsoft.AspNet.Http.Tests/DefaultHttpRequestTests.cs b/test/Microsoft.AspNet.Http.Tests/DefaultHttpRequestTests.cs
index 81b268ba..49de3efc 100644
--- a/test/Microsoft.AspNet.Http.Tests/DefaultHttpRequestTests.cs
+++ b/test/Microsoft.AspNet.Http.Tests/DefaultHttpRequestTests.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNet.Http.Features;
+using Microsoft.AspNet.Primitives;
using Xunit;
namespace Microsoft.AspNet.Http.Internal
@@ -64,9 +65,9 @@ public void Host_GetsHostFromHeaders()
// Arrange
const string expected = "localhost:9001";
- var headers = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ var headers = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
- { "Host", new string[] { expected } },
+ { "Host", expected },
};
var request = CreateRequest(headers);
@@ -84,9 +85,9 @@ public void Host_DecodesPunyCode()
// Arrange
const string expected = "löcalhöst";
- var headers = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ var headers = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
- { "Host", new string[]{ "xn--lcalhst-90ae" } },
+ { "Host", "xn--lcalhst-90ae" },
};
var request = CreateRequest(headers);
@@ -104,7 +105,7 @@ public void Host_EncodesPunyCode()
// Arrange
const string expected = "xn--lcalhst-90ae";
- var headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ var headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
var request = CreateRequest(headers);
@@ -149,9 +150,9 @@ public void Query_GetAndSet()
Assert.Equal("value0", query1["name0"]);
Assert.Equal("value1", query1["name1"]);
- var query2 = new ReadableStringCollection(new Dictionary()
+ var query2 = new ReadableStringCollection(new Dictionary()
{
- { "name2", new[] { "value2" } }
+ { "name2", "value2" }
});
request.Query = query2;
@@ -164,30 +165,30 @@ public void Query_GetAndSet()
public void Cookies_GetAndSet()
{
var request = new DefaultHttpContext().Request;
- var cookieHeaders = request.Headers.GetValues("Cookie");
- Assert.Null(cookieHeaders);
+ var cookieHeaders = request.Headers["Cookie"];
+ Assert.Equal(0, cookieHeaders.Count);
var cookies0 = request.Cookies;
Assert.Equal(0, cookies0.Count);
- request.Headers.SetValues("Cookie", new[] { "name0=value0", "name1=value1" });
+ request.Headers["Cookie"] = new[] { "name0=value0", "name1=value1" };
var cookies1 = request.Cookies;
Assert.Same(cookies0, cookies1);
Assert.Equal(2, cookies1.Count);
Assert.Equal("value0", cookies1["name0"]);
Assert.Equal("value1", cookies1["name1"]);
- var cookies2 = new ReadableStringCollection(new Dictionary()
+ var cookies2 = new ReadableStringCollection(new Dictionary()
{
- { "name2", new[] { "value2" } }
+ { "name2", "value2" }
});
request.Cookies = cookies2;
Assert.Same(cookies2, request.Cookies);
Assert.Equal("value2", request.Cookies["name2"]);
- cookieHeaders = request.Headers.GetValues("Cookie");
+ cookieHeaders = request.Headers["Cookie"];
Assert.Equal(new[] { "name2=value2" }, cookieHeaders);
}
- private static HttpRequest CreateRequest(IDictionary headers)
+ private static HttpRequest CreateRequest(IDictionary headers)
{
var context = new DefaultHttpContext();
context.GetFeature().Headers = headers;
@@ -216,10 +217,10 @@ private static HttpRequest GetRequestWithAcceptCharsetHeader(string acceptCharse
private static HttpRequest GetRequestWithHeader(string headerName, string headerValue)
{
- var headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ var headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
if (headerValue != null)
{
- headers.Add(headerName, new[] { headerValue });
+ headers.Add(headerName, headerValue);
}
return CreateRequest(headers);
diff --git a/test/Microsoft.AspNet.Http.Tests/HeaderDictionaryTests.cs b/test/Microsoft.AspNet.Http.Tests/HeaderDictionaryTests.cs
index 79cbac89..30ac051f 100644
--- a/test/Microsoft.AspNet.Http.Tests/HeaderDictionaryTests.cs
+++ b/test/Microsoft.AspNet.Http.Tests/HeaderDictionaryTests.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using Microsoft.AspNet.Primitives;
using Xunit;
namespace Microsoft.AspNet.Http.Internal
@@ -13,9 +14,9 @@ public class HeaderDictionaryTests
public void PropertiesAreAccessible()
{
var headers = new HeaderDictionary(
- new Dictionary(StringComparer.OrdinalIgnoreCase)
+ new Dictionary(StringComparer.OrdinalIgnoreCase)
{
- { "Header1", new[] { "Value1" } }
+ { "Header1", "Value1" }
});
Assert.Equal(1, headers.Count);
@@ -23,8 +24,7 @@ public void PropertiesAreAccessible()
Assert.True(headers.ContainsKey("header1"));
Assert.False(headers.ContainsKey("header2"));
Assert.Equal("Value1", headers["header1"]);
- Assert.Equal("Value1", headers.Get("header1"));
- Assert.Equal(new[] { "Value1" }, headers.GetValues("header1"));
+ Assert.Equal(new[] { "Value1" }, (string[])headers["header1"]);
}
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Primitives.Tests/Microsoft.AspNet.Primitives.Tests.xproj b/test/Microsoft.AspNet.Primitives.Tests/Microsoft.AspNet.Primitives.Tests.xproj
new file mode 100644
index 00000000..f1a2ba41
--- /dev/null
+++ b/test/Microsoft.AspNet.Primitives.Tests/Microsoft.AspNet.Primitives.Tests.xproj
@@ -0,0 +1,21 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 61f72e92-b3ae-4a10-b838-44f80aed40ae
+ Microsoft.AspNet.Primitives.Tests
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+ 2.0
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Primitives.Tests/StringValuesTests.cs b/test/Microsoft.AspNet.Primitives.Tests/StringValuesTests.cs
new file mode 100644
index 00000000..b46f5ab6
--- /dev/null
+++ b/test/Microsoft.AspNet.Primitives.Tests/StringValuesTests.cs
@@ -0,0 +1,114 @@
+// 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.Linq;
+using Xunit;
+
+namespace Microsoft.AspNet.Primitives
+{
+ public class StringValuesTests
+ {
+ [Fact]
+ public void IsReadOnly_True()
+ {
+ var stringValues = new StringValues();
+ Assert.True(((IList)stringValues).IsReadOnly);
+ Assert.Throws(() => ((IList)stringValues)[0] = string.Empty);
+ Assert.Throws(() => ((ICollection)stringValues).Add(string.Empty));
+ Assert.Throws(() => ((IList)stringValues).Insert(0, string.Empty));
+ Assert.Throws(() => ((ICollection)stringValues).Remove(string.Empty));
+ Assert.Throws(() => ((IList)stringValues).RemoveAt(0));
+ Assert.Throws(() => ((ICollection)stringValues).Clear());
+ }
+
+ [Fact]
+ public void DefaultConstructor_ExpectedValues()
+ {
+ var stringValues = new StringValues();
+ Assert.Equal(0, stringValues.Count);
+ Assert.Equal((string)null, stringValues);
+ Assert.Equal(new string[0], stringValues);
+
+ Assert.True(StringValues.IsNullOrEmpty(stringValues));
+ Assert.Throws(() => stringValues[0]);
+ Assert.Equal(string.Empty, stringValues.ToString());
+ Assert.Equal(-1, ((IList)stringValues).IndexOf(string.Empty));
+ Assert.Equal(0, stringValues.Count());
+ }
+
+ [Fact]
+ public void Constructor_NullStringValue_ExpectedValues()
+ {
+ var stringValues = new StringValues((string)null);
+ Assert.Equal(0, stringValues.Count);
+ Assert.Equal((string)null, stringValues);
+ Assert.Equal(new string[0], stringValues);
+
+ Assert.True(StringValues.IsNullOrEmpty(stringValues));
+ Assert.Throws(() => stringValues[0]);
+ Assert.Equal(string.Empty, stringValues.ToString());
+ Assert.Equal(-1, ((IList)stringValues).IndexOf(string.Empty));
+ Assert.Equal(0, stringValues.Count());
+ }
+
+ [Fact]
+ public void Constructor_NullStringArray_ExpectedValues()
+ {
+ var stringValues = new StringValues((string[])null);
+ Assert.Equal(0, stringValues.Count);
+ Assert.Equal((string)null, stringValues);
+ Assert.Equal(new string[0], stringValues);
+
+ Assert.True(StringValues.IsNullOrEmpty(stringValues));
+ Assert.Throws(() => stringValues[0]);
+ Assert.Equal(string.Empty, stringValues.ToString());
+ Assert.Equal(-1, ((IList)stringValues).IndexOf(string.Empty));
+ Assert.Equal(0, stringValues.Count());
+ }
+
+ [Fact]
+ public void ImplicitStringConverter_Works()
+ {
+ string nullString = null;
+ StringValues stringValues = nullString;
+ Assert.Equal(0, stringValues.Count);
+ Assert.Equal((string)null, stringValues);
+ Assert.Equal(new string[0], stringValues);
+
+ string aString = "abc";
+ stringValues = aString;
+ Assert.Equal(1, stringValues.Count);
+ Assert.Equal(aString, stringValues);
+ Assert.Equal(aString, stringValues[0]);
+ Assert.Equal(new string[] { aString }, stringValues);
+ }
+
+ [Fact]
+ public void ImplicitStringArrayConverter_Works()
+ {
+ string[] nullStringArray = null;
+ StringValues stringValues = nullStringArray;
+ Assert.Equal(0, stringValues.Count);
+ Assert.Equal((string)null, stringValues);
+ Assert.Equal(new string[0], stringValues);
+
+ string aString = "abc";
+ string[] aStringArray = new[] { aString };
+ stringValues = aStringArray;
+ Assert.Equal(1, stringValues.Count);
+ Assert.Equal(aString, stringValues);
+ Assert.Equal(aString, stringValues[0]);
+ Assert.Equal(aStringArray, stringValues);
+
+ aString = "abc";
+ string bString = "bcd";
+ aStringArray = new[] { aString, bString };
+ stringValues = aStringArray;
+ Assert.Equal(2, stringValues.Count);
+ Assert.Equal("abc,bcd", stringValues);
+ Assert.Equal(aStringArray, stringValues);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Primitives.Tests/project.json b/test/Microsoft.AspNet.Primitives.Tests/project.json
new file mode 100644
index 00000000..88f8bbf4
--- /dev/null
+++ b/test/Microsoft.AspNet.Primitives.Tests/project.json
@@ -0,0 +1,13 @@
+{
+ "dependencies": {
+ "Microsoft.AspNet.Primitives": "1.0.0-*",
+ "xunit.runner.aspnet": "2.0.0-aspnet-*"
+ },
+ "commands": {
+ "test": "xunit.runner.aspnet"
+ },
+ "frameworks": {
+ "dnx451": { },
+ "dnxcore50": { }
+ }
+}