#361 Introduce StringValues to replace string[] usage.
This commit is contained in:
parent
15687ab80a
commit
456277fe1d
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.22823.1
|
VisualStudioVersion = 14.0.23107.0
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5A15F1C-885A-452A-A731-B0173DDBD913}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5A15F1C-885A-452A-A731-B0173DDBD913}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -43,6 +43,14 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.WebEnco
|
||||||
EndProject
|
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}"
|
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
|
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.Framework.Primitives", "src\Microsoft.Framework.Primitives\Microsoft.Framework.Primitives.xproj", "{E5FACCD4-6327-43AA-80A9-AE6F4A3BFE6A}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.Primitives.Tests", "test\Microsoft.Framework.Primitives.Tests\Microsoft.Framework.Primitives.Tests.xproj", "{61F72E92-B3AE-4A10-B838-44F80AED40AE}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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|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.ActiveCfg = Release|Any CPU
|
||||||
{68A28E4A-3ADE-4187-9625-4FF185887CB3}.Release|x86.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -268,5 +312,8 @@ Global
|
||||||
{7AE2731D-43CD-4CF8-850A-4914DE2CE930} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21}
|
{7AE2731D-43CD-4CF8-850A-4914DE2CE930} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21}
|
||||||
{BE9112CB-D87D-4080-9CC3-24492D49CBE6} = {A5A15F1C-885A-452A-A731-B0173DDBD913}
|
{BE9112CB-D87D-4080-9CC3-24492D49CBE6} = {A5A15F1C-885A-452A-A731-B0173DDBD913}
|
||||||
{68A28E4A-3ADE-4187-9625-4FF185887CB3} = {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
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.Framework.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>1d0764b4-1deb-4232-a714-d4b7e846918a</ProjectGuid>
|
||||||
|
<RootNamespace>SampleApp</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Http": "1.0.0-*"
|
||||||
|
},
|
||||||
|
|
||||||
|
"commands": {
|
||||||
|
"SampleApp": "SampleApp"
|
||||||
|
},
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnx451": { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,29 +2,30 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http
|
namespace Microsoft.AspNet.Http
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents request and response headers
|
/// Represents request and response headers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IHeaderDictionary : IReadableStringCollection, IDictionary<string, string[]>
|
public interface IHeaderDictionary : IReadableStringCollection, IDictionary<string, StringValues>
|
||||||
{
|
{
|
||||||
|
// This property is duplicated to resolve an ambiguity between IReadableStringCollection and IDictionary<string, StringValues>
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get or sets the associated value from the collection as a single string.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The header name.</param>
|
/// <param name="key"></param>
|
||||||
/// <returns>the associated value from the collection as a single string or null if the key is not present.</returns>
|
/// <returns>The stored value, or StringValues.Empty if the key is not present.</returns>
|
||||||
new string this[string key] { get; set; }
|
new StringValues this[string key] { get; set; }
|
||||||
|
|
||||||
// This property is duplicated to resolve an ambiguity between IReadableStringCollection.Count and IDictionary<string, string[]>.Count
|
// This property is duplicated to resolve an ambiguity between IReadableStringCollection.Count and IDictionary<string, StringValues>.Count
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the number of elements contained in the collection.
|
/// Gets the number of elements contained in the collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
new int Count { get; }
|
new int Count { get; }
|
||||||
|
|
||||||
// This property is duplicated to resolve an ambiguity between IReadableStringCollection.Keys and IDictionary<string, string[]>.Keys
|
// This property is duplicated to resolve an ambiguity between IReadableStringCollection.Keys and IDictionary<string, StringValues>.Keys
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a collection containing the keys.
|
/// Gets a collection containing the keys.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -36,21 +37,14 @@ namespace Microsoft.AspNet.Http
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The header name.</param>
|
/// <param name="key">The header name.</param>
|
||||||
/// <returns>the associated values from the collection separated into individual values, or null if the key is not present.</returns>
|
/// <returns>the associated values from the collection separated into individual values, or null if the key is not present.</returns>
|
||||||
IList<string> GetCommaSeparatedValues(string key);
|
StringValues GetCommaSeparatedValues(string key);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a new value. Appends to the header if already present
|
/// Add a new value. Appends to the header list if already present
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The header name.</param>
|
/// <param name="key">The header name.</param>
|
||||||
/// <param name="value">The header value.</param>
|
/// <param name="value">The header value.</param>
|
||||||
void Append(string key, string value);
|
void Append(string key, StringValues value);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add new values. Each item remains a separate array entry.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <param name="values">The header values.</param>
|
|
||||||
void AppendValues(string key, params string[] values);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Quotes any values containing comas, and then coma joins all of the values with any existing values.
|
/// Quotes any values containing comas, and then coma joins all of the values with any existing values.
|
||||||
|
|
@ -59,21 +53,6 @@ namespace Microsoft.AspNet.Http
|
||||||
/// <param name="values">The header values.</param>
|
/// <param name="values">The header values.</param>
|
||||||
void AppendCommaSeparatedValues(string key, params string[] values);
|
void AppendCommaSeparatedValues(string key, params string[] values);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a specific header value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <param name="value">The header value.</param>
|
|
||||||
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Set", Justification = "Re-evaluate later.")]
|
|
||||||
void Set(string key, string value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the specified header values without modification.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <param name="values">The header values.</param>
|
|
||||||
void SetValues(string key, params string[] values);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Quotes any values containing comas, and then coma joins all of the values.
|
/// Quotes any values containing comas, and then coma joins all of the values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,22 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http
|
namespace Microsoft.AspNet.Http
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Accessors for headers, query, forms, etc.
|
/// Accessors for headers, query, forms, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IReadableStringCollection : IEnumerable<KeyValuePair<string, string[]>>
|
public interface IReadableStringCollection : IEnumerable<KeyValuePair<string, StringValues>>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the associated value from the collection. Multiple values will be merged.
|
/// Get the associated value from the collection.
|
||||||
/// Returns null if the key is not present.
|
/// Returns StringValues.Empty if the key is not present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
string this[string key] { get; }
|
StringValues this[string key] { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the number of elements contained in the collection.
|
/// Gets the number of elements contained in the collection.
|
||||||
|
|
@ -35,22 +35,5 @@ namespace Microsoft.AspNet.Http
|
||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
bool ContainsKey(string key);
|
bool ContainsKey(string key);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the associated value from the collection. Multiple values will be merged.
|
|
||||||
/// Returns null if the key is not present.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "Re-evaluate later.")]
|
|
||||||
string Get(string key);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the associated values from the collection in their original format.
|
|
||||||
/// Returns null if the key is not present.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
IList<string> GetValues(string key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Microsoft.Framework.WebEncoders;
|
using Microsoft.Framework.WebEncoders;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http
|
namespace Microsoft.AspNet.Http
|
||||||
|
|
@ -143,7 +144,7 @@ namespace Microsoft.AspNet.Http
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parameters"></param>
|
/// <param name="parameters"></param>
|
||||||
/// <returns>The resulting QueryString</returns>
|
/// <returns>The resulting QueryString</returns>
|
||||||
public static QueryString Create(IEnumerable<KeyValuePair<string, string[]>> parameters)
|
public static QueryString Create(IEnumerable<KeyValuePair<string, StringValues>> parameters)
|
||||||
{
|
{
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Http.Headers;
|
using Microsoft.AspNet.Http.Headers;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http
|
namespace Microsoft.AspNet.Http
|
||||||
|
|
@ -50,7 +51,7 @@ namespace Microsoft.AspNet.Http
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
headers.SetValues(name, values.Select(value => value.ToString()).ToArray());
|
headers[name] = values.Select(value => value.ToString()).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,7 +99,7 @@ namespace Microsoft.AspNet.Http
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = headers[name];
|
var value = headers[name];
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (StringValues.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
@ -112,11 +113,11 @@ namespace Microsoft.AspNet.Http
|
||||||
if (KnownListParsers.TryGetValue(typeof(T), out temp))
|
if (KnownListParsers.TryGetValue(typeof(T), out temp))
|
||||||
{
|
{
|
||||||
var func = (Func<IList<string>, IList<T>>)temp;
|
var func = (Func<IList<string>, IList<T>>)temp;
|
||||||
return func(headers.GetValues(name));
|
return func(headers[name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var values = headers.GetValues(name);
|
var values = headers[name];
|
||||||
if (values == null || !values.Any())
|
if (StringValues.IsNullOrEmpty(values))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +159,7 @@ namespace Microsoft.AspNet.Http
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IList<T> GetListViaReflection<T>(IList<string> values)
|
private static IList<T> GetListViaReflection<T>(StringValues values)
|
||||||
{
|
{
|
||||||
// TODO: Cache the reflected type for later? Only if success?
|
// TODO: Cache the reflected type for later? Only if success?
|
||||||
var type = typeof(T);
|
var type = typeof(T);
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,7 @@ namespace Microsoft.AspNet.Http.Headers
|
||||||
|
|
||||||
public void AppendList<T>([NotNull] string name, [NotNull] IList<T> values)
|
public void AppendList<T>([NotNull] string name, [NotNull] IList<T> values)
|
||||||
{
|
{
|
||||||
Headers.AppendValues(name, values.Select(value => value.ToString()).ToArray());
|
Headers.Append(name, values.Select(value => value.ToString()).ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +182,7 @@ namespace Microsoft.AspNet.Http.Headers
|
||||||
|
|
||||||
public void AppendList<T>([NotNull] string name, [NotNull] IList<T> values)
|
public void AppendList<T>([NotNull] string name, [NotNull] IList<T> values)
|
||||||
{
|
{
|
||||||
Headers.AppendValues(name, values.Select(value => value.ToString()).ToArray());
|
Headers.Append(name, values.Select(value => value.ToString()).ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features
|
namespace Microsoft.AspNet.Http.Features
|
||||||
{
|
{
|
||||||
|
|
@ -14,7 +15,7 @@ namespace Microsoft.AspNet.Http.Features
|
||||||
string PathBase { get; set; }
|
string PathBase { get; set; }
|
||||||
string Path { get; set; }
|
string Path { get; set; }
|
||||||
string QueryString { get; set; }
|
string QueryString { get; set; }
|
||||||
IDictionary<string, string[]> Headers { get; set; }
|
IDictionary<string, StringValues> Headers { get; set; }
|
||||||
Stream Body { get; set; }
|
Stream Body { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features
|
namespace Microsoft.AspNet.Http.Features
|
||||||
{
|
{
|
||||||
|
|
@ -12,7 +13,7 @@ namespace Microsoft.AspNet.Http.Features
|
||||||
{
|
{
|
||||||
int StatusCode { get; set; }
|
int StatusCode { get; set; }
|
||||||
string ReasonPhrase { get; set; }
|
string ReasonPhrase { get; set; }
|
||||||
IDictionary<string, string[]> Headers { get; set; }
|
IDictionary<string, StringValues> Headers { get; set; }
|
||||||
Stream Body { get; set; }
|
Stream Body { get; set; }
|
||||||
bool HasStarted { get; }
|
bool HasStarted { get; }
|
||||||
void OnStarting(Func<object, Task> callback, object state);
|
void OnStarting(Func<object, Task> callback, object state);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,11 @@
|
||||||
"url": "git://github.com/aspnet/httpabstractions"
|
"url": "git://github.com/aspnet/httpabstractions"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }
|
"Microsoft.Framework.NotNullAttribute.Sources": {
|
||||||
|
"type": "build",
|
||||||
|
"version": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"Microsoft.Framework.Primitives": "1.0.0-*"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"dnx451": { },
|
"dnx451": { },
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var contentType = Headers[HeaderNames.ContentType];
|
return Headers[HeaderNames.ContentType];
|
||||||
return contentType;
|
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
|
@ -79,7 +78,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HttpResponseFeature.Headers[HeaderNames.ContentType] = new[] { value };
|
HttpResponseFeature.Headers[HeaderNames.ContentType] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +114,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
HttpResponseFeature.StatusCode = 302;
|
HttpResponseFeature.StatusCode = 302;
|
||||||
}
|
}
|
||||||
|
|
||||||
Headers.Set(HeaderNames.Location, location);
|
Headers[HeaderNames.Location] = location;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,8 +12,6 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
public class DefaultWebSocketManager : WebSocketManager
|
public class DefaultWebSocketManager : WebSocketManager
|
||||||
{
|
{
|
||||||
private static IList<string> EmptyList = new List<string>();
|
|
||||||
|
|
||||||
private IFeatureCollection _features;
|
private IFeatureCollection _features;
|
||||||
private FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
|
private FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
|
||||||
private FeatureReference<IHttpWebSocketFeature> _webSockets = FeatureReference<IHttpWebSocketFeature>.Default;
|
private FeatureReference<IHttpWebSocketFeature> _webSockets = FeatureReference<IHttpWebSocketFeature>.Default;
|
||||||
|
|
@ -45,8 +43,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return ParsingHelpers.GetHeaderUnmodified(HttpRequestFeature.Headers,
|
return ParsingHelpers.GetHeaderSplit(HttpRequestFeature.Headers, HeaderNames.WebSocketSubProtocols);
|
||||||
HeaderNames.WebSocketSubProtocols) ?? EmptyList;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.AspNet.WebUtilities;
|
using Microsoft.AspNet.WebUtilities;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features.Internal
|
namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
|
|
@ -87,7 +88,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
|
|
||||||
_request.EnableRewind();
|
_request.EnableRewind();
|
||||||
|
|
||||||
IDictionary<string, string[]> formFields = null;
|
IDictionary<string, StringValues> formFields = null;
|
||||||
var files = new FormFileCollection();
|
var files = new FormFileCollection();
|
||||||
|
|
||||||
// Some of these code paths use StreamReader which does not support cancellation tokens.
|
// Some of these code paths use StreamReader which does not support cancellation tokens.
|
||||||
|
|
@ -102,7 +103,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
}
|
}
|
||||||
else if (HasMultipartFormContentType(contentType))
|
else if (HasMultipartFormContentType(contentType))
|
||||||
{
|
{
|
||||||
var formAccumulator = new KeyValueAccumulator<string, string>(StringComparer.OrdinalIgnoreCase);
|
var formAccumulator = new KeyValueAccumulator();
|
||||||
|
|
||||||
var boundary = GetBoundary(contentType);
|
var boundary = GetBoundary(contentType);
|
||||||
var multipartReader = new MultipartReader(boundary, _request.Body);
|
var multipartReader = new MultipartReader(boundary, _request.Body);
|
||||||
|
|
@ -111,7 +112,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
var headers = new HeaderDictionary(section.Headers);
|
var headers = new HeaderDictionary(section.Headers);
|
||||||
ContentDispositionHeaderValue contentDisposition;
|
ContentDispositionHeaderValue contentDisposition;
|
||||||
ContentDispositionHeaderValue.TryParse(headers.Get(HeaderNames.ContentDisposition), out contentDisposition);
|
ContentDispositionHeaderValue.TryParse(headers[HeaderNames.ContentDisposition], out contentDisposition);
|
||||||
if (HasFileContentDisposition(contentDisposition))
|
if (HasFileContentDisposition(contentDisposition))
|
||||||
{
|
{
|
||||||
// Find the end
|
// Find the end
|
||||||
|
|
@ -131,7 +132,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
|
|
||||||
var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name);
|
var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name);
|
||||||
MediaTypeHeaderValue mediaType;
|
MediaTypeHeaderValue mediaType;
|
||||||
MediaTypeHeaderValue.TryParse(headers.Get(HeaderNames.ContentType), out mediaType);
|
MediaTypeHeaderValue.TryParse(headers[HeaderNames.ContentType], out mediaType);
|
||||||
var encoding = FilterEncoding(mediaType?.Encoding);
|
var encoding = FilterEncoding(mediaType?.Encoding);
|
||||||
using (var reader = new StreamReader(section.Body, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true))
|
using (var reader = new StreamReader(section.Body, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true))
|
||||||
{
|
{
|
||||||
|
|
@ -141,7 +142,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + headers.Get(HeaderNames.ContentDisposition));
|
System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + headers[HeaderNames.ContentDisposition]);
|
||||||
}
|
}
|
||||||
|
|
||||||
section = await multipartReader.ReadNextSectionAsync(cancellationToken);
|
section = await multipartReader.ReadNextSectionAsync(cancellationToken);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features.Internal
|
namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -11,7 +12,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
public HttpRequestFeature()
|
public HttpRequestFeature()
|
||||||
{
|
{
|
||||||
Headers = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
Headers = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
|
||||||
Body = Stream.Null;
|
Body = Stream.Null;
|
||||||
Protocol = string.Empty;
|
Protocol = string.Empty;
|
||||||
Scheme = string.Empty;
|
Scheme = string.Empty;
|
||||||
|
|
@ -27,7 +28,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
public string PathBase { get; set; }
|
public string PathBase { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public string QueryString { get; set; }
|
public string QueryString { get; set; }
|
||||||
public IDictionary<string, string[]> Headers { get; set; }
|
public IDictionary<string, StringValues> Headers { get; set; }
|
||||||
public Stream Body { get; set; }
|
public Stream Body { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features.Internal
|
namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -13,7 +14,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
public HttpResponseFeature()
|
public HttpResponseFeature()
|
||||||
{
|
{
|
||||||
StatusCode = 200;
|
StatusCode = 200;
|
||||||
Headers = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
Headers = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
|
||||||
Body = Stream.Null;
|
Body = Stream.Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,7 +22,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
|
|
||||||
public string ReasonPhrase { get; set; }
|
public string ReasonPhrase { get; set; }
|
||||||
|
|
||||||
public IDictionary<string, string[]> Headers { get; set; }
|
public IDictionary<string, StringValues> Headers { get; set; }
|
||||||
|
|
||||||
public Stream Body { get; set; }
|
public Stream Body { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.AspNet.WebUtilities;
|
using Microsoft.AspNet.WebUtilities;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features.Internal
|
namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -13,17 +14,18 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
private readonly IFeatureCollection _features;
|
private readonly IFeatureCollection _features;
|
||||||
private FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
|
private FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
|
||||||
private string _queryString;
|
|
||||||
private IReadableStringCollection _query;
|
|
||||||
|
|
||||||
public QueryFeature([NotNull] IDictionary<string, string[]> query)
|
private string _original;
|
||||||
: this (new ReadableStringCollection(query))
|
private IReadableStringCollection _parsedValues;
|
||||||
|
|
||||||
|
public QueryFeature([NotNull] IDictionary<string, StringValues> query)
|
||||||
|
: this(new ReadableStringCollection(query))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryFeature([NotNull] IReadableStringCollection query)
|
public QueryFeature([NotNull] IReadableStringCollection query)
|
||||||
{
|
{
|
||||||
_query = query;
|
_parsedValues = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryFeature([NotNull] IFeatureCollection features)
|
public QueryFeature([NotNull] IFeatureCollection features)
|
||||||
|
|
@ -37,24 +39,32 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
if (_features == null)
|
if (_features == null)
|
||||||
{
|
{
|
||||||
return _query;
|
return _parsedValues ?? ReadableStringCollection.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryString = _request.Fetch(_features).QueryString;
|
var current = _request.Fetch(_features).QueryString;
|
||||||
if (_query == null || !string.Equals(_queryString, queryString, StringComparison.Ordinal))
|
if (_parsedValues == null || !string.Equals(_original, current, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
_queryString = queryString;
|
_original = current;
|
||||||
_query = new ReadableStringCollection(QueryHelpers.ParseQuery(queryString));
|
_parsedValues = new ReadableStringCollection(QueryHelpers.ParseQuery(current));
|
||||||
}
|
}
|
||||||
return _query;
|
return _parsedValues;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_query = value;
|
_parsedValues = value;
|
||||||
if (_features != null)
|
if (_features != null)
|
||||||
{
|
{
|
||||||
_queryString = _query == null ? string.Empty : QueryString.Create(_query).ToString();
|
if (value == null)
|
||||||
_request.Fetch(_features).QueryString = _queryString;
|
{
|
||||||
|
_original = string.Empty;
|
||||||
|
_request.Fetch(_features).QueryString = string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_original = QueryString.Create(_parsedValues).ToString();
|
||||||
|
_request.Fetch(_features).QueryString = _original;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features.Internal
|
namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
|
|
@ -14,18 +15,18 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
private readonly IFeatureCollection _features;
|
private readonly IFeatureCollection _features;
|
||||||
private readonly FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
|
private readonly FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
|
||||||
private string[] _cookieHeaders;
|
|
||||||
private RequestCookiesCollection _cookiesCollection;
|
|
||||||
private IReadableStringCollection _cookies;
|
|
||||||
|
|
||||||
public RequestCookiesFeature([NotNull] IDictionary<string, string[]> cookies)
|
private StringValues _original;
|
||||||
: this (new ReadableStringCollection(cookies))
|
private IReadableStringCollection _parsedValues;
|
||||||
|
|
||||||
|
public RequestCookiesFeature([NotNull] IDictionary<string, StringValues> cookies)
|
||||||
|
: this(new ReadableStringCollection(cookies))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public RequestCookiesFeature([NotNull] IReadableStringCollection cookies)
|
public RequestCookiesFeature([NotNull] IReadableStringCollection cookies)
|
||||||
{
|
{
|
||||||
_cookies = cookies;
|
_parsedValues = cookies;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RequestCookiesFeature([NotNull] IFeatureCollection features)
|
public RequestCookiesFeature([NotNull] IFeatureCollection features)
|
||||||
|
|
@ -39,46 +40,53 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
if (_features == null)
|
if (_features == null)
|
||||||
{
|
{
|
||||||
return _cookies;
|
return _parsedValues ?? ReadableStringCollection.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
var headers = _request.Fetch(_features).Headers;
|
var headers = _request.Fetch(_features).Headers;
|
||||||
string[] values;
|
StringValues current;
|
||||||
if (!headers.TryGetValue(HeaderNames.Cookie, out values))
|
if (!headers.TryGetValue(HeaderNames.Cookie, out current))
|
||||||
{
|
{
|
||||||
values = new string[0];
|
current = StringValues.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_cookieHeaders == null || !Enumerable.SequenceEqual(_cookieHeaders, values, StringComparer.Ordinal))
|
if (_parsedValues == null || !Enumerable.SequenceEqual(_original, current, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
_cookieHeaders = values;
|
_original = current;
|
||||||
if (_cookiesCollection == null)
|
var collectionParser = _parsedValues as RequestCookiesCollection;
|
||||||
|
if (collectionParser == null)
|
||||||
{
|
{
|
||||||
_cookiesCollection = new RequestCookiesCollection();
|
collectionParser = new RequestCookiesCollection();
|
||||||
_cookies = _cookiesCollection;
|
_parsedValues = collectionParser;
|
||||||
}
|
}
|
||||||
_cookiesCollection.Reparse(values);
|
collectionParser.Reparse(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _cookies;
|
return _parsedValues;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_cookies = value;
|
_parsedValues = value;
|
||||||
_cookieHeaders = null;
|
_original = StringValues.Empty;
|
||||||
_cookiesCollection = _cookies as RequestCookiesCollection;
|
if (_features != null)
|
||||||
if (_cookies != null && _features != null)
|
|
||||||
{
|
{
|
||||||
var headers = new List<string>();
|
if (_parsedValues == null || _parsedValues.Count == 0)
|
||||||
foreach (var pair in _cookies)
|
|
||||||
{
|
{
|
||||||
foreach (var cookieValue in pair.Value)
|
_request.Fetch(_features).Headers.Remove(HeaderNames.Cookie);
|
||||||
{
|
}
|
||||||
headers.Add(new CookieHeaderValue(pair.Key, cookieValue).ToString());
|
else
|
||||||
}
|
{
|
||||||
|
var headers = new List<string>();
|
||||||
|
foreach (var pair in _parsedValues)
|
||||||
|
{
|
||||||
|
foreach (var cookieValue in pair.Value)
|
||||||
|
{
|
||||||
|
headers.Add(new CookieHeaderValue(pair.Key, cookieValue).ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_original = headers.ToArray();
|
||||||
|
_request.Fetch(_features).Headers[HeaderNames.Cookie] = _original;
|
||||||
}
|
}
|
||||||
_cookieHeaders = headers.ToArray();
|
|
||||||
_request.Fetch(_features).Headers[HeaderNames.Cookie] = _cookieHeaders;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -11,12 +12,12 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FormCollection : ReadableStringCollection, IFormCollection
|
public class FormCollection : ReadableStringCollection, IFormCollection
|
||||||
{
|
{
|
||||||
public FormCollection([NotNull] IDictionary<string, string[]> store)
|
public FormCollection([NotNull] IDictionary<string, StringValues> store)
|
||||||
: this(store, new FormFileCollection())
|
: this(store, new FormFileCollection())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormCollection([NotNull] IDictionary<string, string[]> store, [NotNull] IFormFileCollection files)
|
public FormCollection([NotNull] IDictionary<string, StringValues> store, [NotNull] IFormFileCollection files)
|
||||||
: base(store)
|
: base(store)
|
||||||
{
|
{
|
||||||
Files = files;
|
Files = files;
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNet.Http.Internal;
|
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -15,7 +14,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class HeaderDictionary : IHeaderDictionary
|
public class HeaderDictionary : IHeaderDictionary
|
||||||
{
|
{
|
||||||
public HeaderDictionary() : this(new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase))
|
public HeaderDictionary() : this(new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,12 +22,12 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// Initializes a new instance of the <see cref="T:Microsoft.Owin.HeaderDictionary" /> class.
|
/// Initializes a new instance of the <see cref="T:Microsoft.Owin.HeaderDictionary" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="store">The underlying data store.</param>
|
/// <param name="store">The underlying data store.</param>
|
||||||
public HeaderDictionary([NotNull] IDictionary<string, string[]> store)
|
public HeaderDictionary([NotNull] IDictionary<string, StringValues> store)
|
||||||
{
|
{
|
||||||
Store = store;
|
Store = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDictionary<string, string[]> Store { get; set; }
|
private IDictionary<string, StringValues> Store { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an <see cref="T:System.Collections.ICollection" /> that contains the keys in the <see cref="T:Microsoft.Owin.HeaderDictionary" />;.
|
/// Gets an <see cref="T:System.Collections.ICollection" /> that contains the keys in the <see cref="T:Microsoft.Owin.HeaderDictionary" />;.
|
||||||
|
|
@ -42,7 +41,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ICollection<string[]> Values
|
public ICollection<StringValues> Values
|
||||||
{
|
{
|
||||||
get { return Store.Values; }
|
get { return Store.Values; }
|
||||||
}
|
}
|
||||||
|
|
@ -69,11 +68,11 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// Get or sets the associated value from the collection as a single string.
|
/// Get or sets the associated value from the collection as a single string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The header name.</param>
|
/// <param name="key">The header name.</param>
|
||||||
/// <returns>the associated value from the collection as a single string or null if the key is not present.</returns>
|
/// <returns>the associated value from the collection as a StringValues or StringValues.Empty if the key is not present.</returns>
|
||||||
public string this[string key]
|
public StringValues this[string key]
|
||||||
{
|
{
|
||||||
get { return Get(key); }
|
get { return ParsingHelpers.GetHeader(Store, key); }
|
||||||
set { Set(key, value); }
|
set { ParsingHelpers.SetHeader(Store, key, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -81,7 +80,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The header name.</param>
|
/// <param name="key">The header name.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
string[] IDictionary<string, string[]>.this[string key]
|
StringValues IDictionary<string, StringValues>.this[string key]
|
||||||
{
|
{
|
||||||
get { return Store[key]; }
|
get { return Store[key]; }
|
||||||
set { Store[key] = value; }
|
set { Store[key] = value; }
|
||||||
|
|
@ -91,7 +90,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// Returns an enumerator that iterates through a collection.
|
/// Returns an enumerator that iterates through a collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
|
IEnumerator<KeyValuePair<string, StringValues>> IEnumerable<KeyValuePair<string, StringValues>>.GetEnumerator()
|
||||||
{
|
{
|
||||||
return Store.GetEnumerator();
|
return Store.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
@ -102,59 +101,29 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
{
|
{
|
||||||
return GetEnumerator();
|
return Store.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the associated value from the collection as a single string.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <returns>the associated value from the collection as a single string or null if the key is not present.</returns>
|
|
||||||
public string Get(string key)
|
|
||||||
{
|
|
||||||
return ParsingHelpers.GetHeader(Store, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the associated values from the collection without modification.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <returns>the associated value from the collection without modification, or null if the key is not present.</returns>
|
|
||||||
public IList<string> GetValues(string key)
|
|
||||||
{
|
|
||||||
return ParsingHelpers.GetHeaderUnmodified(Store, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the associated values from the collection separated into individual values.
|
/// Get the associated values from the collection separated into individual values.
|
||||||
/// Quoted values will not be split, and the quotes will be removed.
|
/// Quoted values will not be split, and the quotes will be removed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The header name.</param>
|
/// <param name="key">The header name.</param>
|
||||||
/// <returns>the associated values from the collection separated into individual values, or null if the key is not present.</returns>
|
/// <returns>the associated values from the collection separated into individual values, or StringValues.Empty if the key is not present.</returns>
|
||||||
public IList<string> GetCommaSeparatedValues(string key)
|
public StringValues GetCommaSeparatedValues(string key)
|
||||||
{
|
{
|
||||||
IEnumerable<string> values = ParsingHelpers.GetHeaderSplit(Store, key);
|
return ParsingHelpers.GetHeaderSplit(Store, key);
|
||||||
return values == null ? null : values.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a new value. Appends to the header if already present
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <param name="value">The header value.</param>
|
|
||||||
public void Append(string key, string value)
|
|
||||||
{
|
|
||||||
ParsingHelpers.AppendHeader(Store, key, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add new values. Each item remains a separate array entry.
|
/// Add new values. Each item remains a separate array entry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The header name.</param>
|
/// <param name="key">The header name.</param>
|
||||||
/// <param name="values">The header values.</param>
|
/// <param name="value">The header value.</param>
|
||||||
public void AppendValues(string key, params string[] values)
|
public void Append(string key, StringValues value)
|
||||||
{
|
{
|
||||||
ParsingHelpers.AppendHeaderUnmodified(Store, key, values);
|
ParsingHelpers.AppendHeaderUnmodified(Store, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -167,26 +136,6 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
ParsingHelpers.AppendHeaderJoined(Store, key, values);
|
ParsingHelpers.AppendHeaderJoined(Store, key, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a specific header value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <param name="value">The header value.</param>
|
|
||||||
public void Set(string key, string value)
|
|
||||||
{
|
|
||||||
ParsingHelpers.SetHeader(Store, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the specified header values without modification.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <param name="values">The header values.</param>
|
|
||||||
public void SetValues(string key, params string[] values)
|
|
||||||
{
|
|
||||||
ParsingHelpers.SetHeaderUnmodified(Store, key, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Quotes any values containing comas, and then coma joins all of the values.
|
/// Quotes any values containing comas, and then coma joins all of the values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -202,7 +151,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The header name.</param>
|
/// <param name="key">The header name.</param>
|
||||||
/// <param name="value">The header values.</param>
|
/// <param name="value">The header values.</param>
|
||||||
public void Add(string key, string[] value)
|
public void Add(string key, StringValues value)
|
||||||
{
|
{
|
||||||
Store.Add(key, value);
|
Store.Add(key, value);
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +182,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <param name="key">The header name.</param>
|
/// <param name="key">The header name.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <returns>true if the <see cref="T:Microsoft.Owin.HeaderDictionary" /> contains the key; otherwise, false.</returns>
|
/// <returns>true if the <see cref="T:Microsoft.Owin.HeaderDictionary" /> contains the key; otherwise, false.</returns>
|
||||||
public bool TryGetValue(string key, out string[] value)
|
public bool TryGetValue(string key, out StringValues value)
|
||||||
{
|
{
|
||||||
return Store.TryGetValue(key, out value);
|
return Store.TryGetValue(key, out value);
|
||||||
}
|
}
|
||||||
|
|
@ -242,7 +191,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// Adds a new list of items to the collection.
|
/// Adds a new list of items to the collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item to add.</param>
|
/// <param name="item">The item to add.</param>
|
||||||
public void Add(KeyValuePair<string, string[]> item)
|
public void Add(KeyValuePair<string, StringValues> item)
|
||||||
{
|
{
|
||||||
Store.Add(item);
|
Store.Add(item);
|
||||||
}
|
}
|
||||||
|
|
@ -260,7 +209,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <returns>true if the specified object occurs within this collection; otherwise, false.</returns>
|
/// <returns>true if the specified object occurs within this collection; otherwise, false.</returns>
|
||||||
public bool Contains(KeyValuePair<string, string[]> item)
|
public bool Contains(KeyValuePair<string, StringValues> item)
|
||||||
{
|
{
|
||||||
return Store.Contains(item);
|
return Store.Contains(item);
|
||||||
}
|
}
|
||||||
|
|
@ -270,7 +219,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="array">The one-dimensional Array that is the destination of the specified objects copied from the <see cref="T:Microsoft.Owin.HeaderDictionary" />.</param>
|
/// <param name="array">The one-dimensional Array that is the destination of the specified objects copied from the <see cref="T:Microsoft.Owin.HeaderDictionary" />.</param>
|
||||||
/// <param name="arrayIndex">The zero-based index in <paramref name="array" /> at which copying begins.</param>
|
/// <param name="arrayIndex">The zero-based index in <paramref name="array" /> at which copying begins.</param>
|
||||||
public void CopyTo(KeyValuePair<string, string[]>[] array, int arrayIndex)
|
public void CopyTo(KeyValuePair<string, StringValues>[] array, int arrayIndex)
|
||||||
{
|
{
|
||||||
Store.CopyTo(array, arrayIndex);
|
Store.CopyTo(array, arrayIndex);
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +229,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
|
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
|
||||||
public bool Remove(KeyValuePair<string, string[]> item)
|
public bool Remove(KeyValuePair<string, StringValues> item)
|
||||||
{
|
{
|
||||||
return Store.Remove(item);
|
return Store.Remove(item);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
@ -76,9 +77,9 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
[System.CodeDom.Compiler.GeneratedCode("App_Packages", "")]
|
[System.CodeDom.Compiler.GeneratedCode("App_Packages", "")]
|
||||||
internal struct HeaderSegmentCollection : IEnumerable<HeaderSegment>, IEquatable<HeaderSegmentCollection>
|
internal struct HeaderSegmentCollection : IEnumerable<HeaderSegment>, IEquatable<HeaderSegmentCollection>
|
||||||
{
|
{
|
||||||
private readonly string[] _headers;
|
private readonly StringValues _headers;
|
||||||
|
|
||||||
public HeaderSegmentCollection(string[] headers)
|
public HeaderSegmentCollection(StringValues headers)
|
||||||
{
|
{
|
||||||
_headers = headers;
|
_headers = headers;
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +103,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
public override int GetHashCode()
|
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)
|
public static bool operator ==(HeaderSegmentCollection left, HeaderSegmentCollection right)
|
||||||
|
|
@ -134,7 +135,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
internal struct Enumerator : IEnumerator<HeaderSegment>
|
internal struct Enumerator : IEnumerator<HeaderSegment>
|
||||||
{
|
{
|
||||||
private readonly string[] _headers;
|
private readonly StringValues _headers;
|
||||||
private int _index;
|
private int _index;
|
||||||
|
|
||||||
private string _header;
|
private string _header;
|
||||||
|
|
@ -149,11 +150,9 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
private Mode _mode;
|
private Mode _mode;
|
||||||
|
|
||||||
private static readonly string[] NoHeaders = new string[0];
|
public Enumerator(StringValues headers)
|
||||||
|
|
||||||
public Enumerator(string[] headers)
|
|
||||||
{
|
{
|
||||||
_headers = headers ?? NoHeaders;
|
_headers = headers;
|
||||||
_header = string.Empty;
|
_header = string.Empty;
|
||||||
_headerLength = -1;
|
_headerLength = -1;
|
||||||
_index = -1;
|
_index = -1;
|
||||||
|
|
@ -237,7 +236,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
_trailingStart = -1;
|
_trailingStart = -1;
|
||||||
|
|
||||||
// if that was the last string
|
// if that was the last string
|
||||||
if (_index == _headers.Length)
|
if (_index == _headers.Count)
|
||||||
{
|
{
|
||||||
// no more move nexts
|
// no more move nexts
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -496,19 +495,19 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
internal static class ParsingHelpers
|
internal static class ParsingHelpers
|
||||||
{
|
{
|
||||||
public static string GetHeader(IDictionary<string, string[]> headers, string key)
|
public static StringValues GetHeader(IDictionary<string, StringValues> headers, string key)
|
||||||
{
|
{
|
||||||
string[] values = GetHeaderUnmodified(headers, key);
|
StringValues value;
|
||||||
return values == null ? null : string.Join(",", values);
|
return headers.TryGetValue(key, out value) ? value : StringValues.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<string> GetHeaderSplit(IDictionary<string, string[]> headers, string key)
|
public static StringValues GetHeaderSplit(IDictionary<string, StringValues> headers, string key)
|
||||||
{
|
{
|
||||||
string[] values = GetHeaderUnmodified(headers, key);
|
var values = GetHeaderUnmodified(headers, key);
|
||||||
return values == null ? null : GetHeaderSplitImplementation(values);
|
return new StringValues(GetHeaderSplitImplementation(values).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<string> GetHeaderSplitImplementation(string[] values)
|
private static IEnumerable<string> GetHeaderSplitImplementation(StringValues values)
|
||||||
{
|
{
|
||||||
foreach (var segment in new HeaderSegmentCollection(values))
|
foreach (var segment in new HeaderSegmentCollection(values))
|
||||||
{
|
{
|
||||||
|
|
@ -519,41 +518,41 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string[] GetHeaderUnmodified([NotNull] IDictionary<string, string[]> headers, string key)
|
public static StringValues GetHeaderUnmodified([NotNull] IDictionary<string, StringValues> headers, string key)
|
||||||
{
|
{
|
||||||
string[] values;
|
StringValues values;
|
||||||
return headers.TryGetValue(key, out values) ? values : null;
|
return headers.TryGetValue(key, out values) ? values : StringValues.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetHeader([NotNull] IDictionary<string, string[]> headers, [NotNull] string key, string value)
|
public static void SetHeader([NotNull] IDictionary<string, StringValues> headers, [NotNull] string key, StringValues value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(key))
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (StringValues.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
headers.Remove(key);
|
headers.Remove(key);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
headers[key] = new[] { value };
|
headers[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetHeaderJoined([NotNull] IDictionary<string, string[]> headers, [NotNull] string key, params string[] values)
|
public static void SetHeaderJoined([NotNull] IDictionary<string, StringValues> headers, [NotNull] string key, StringValues value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(key))
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
if (values == null || values.Length == 0)
|
if (StringValues.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
headers.Remove(key);
|
headers.Remove(key);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
headers[key] = new[] { string.Join(",", values.Select(value => QuoteIfNeeded(value))) };
|
headers[key] = string.Join(",", value.Select(QuoteIfNeeded));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -589,46 +588,23 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetHeaderUnmodified([NotNull] IDictionary<string, string[]> headers, [NotNull] string key, params string[] values)
|
public static void SetHeaderUnmodified([NotNull] IDictionary<string, StringValues> headers, [NotNull] string key, StringValues? values)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(key))
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
if (values == null || values.Length == 0)
|
if (!values.HasValue || StringValues.IsNullOrEmpty(values.Value))
|
||||||
{
|
{
|
||||||
headers.Remove(key);
|
headers.Remove(key);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
headers[key] = values;
|
headers[key] = values.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetHeaderUnmodified([NotNull] IDictionary<string, string[]> headers, [NotNull] string key, [NotNull] IEnumerable<string> values)
|
public static void AppendHeaderJoined([NotNull] IDictionary<string, StringValues> headers, [NotNull] string key, params string[] values)
|
||||||
{
|
|
||||||
headers[key] = values.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AppendHeader([NotNull] IDictionary<string, string[]> 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 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AppendHeaderJoined([NotNull] IDictionary<string, string[]> headers, [NotNull] string key, params string[] values)
|
|
||||||
{
|
{
|
||||||
if (values == null || values.Length == 0)
|
if (values == null || values.Length == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -642,35 +618,29 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
else
|
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<string, string[]> headers, [NotNull] string key, params string[] values)
|
public static void AppendHeaderUnmodified([NotNull] IDictionary<string, StringValues> headers, [NotNull] string key, StringValues values)
|
||||||
{
|
{
|
||||||
if (values == null || values.Length == 0)
|
if (values.Count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] existing = GetHeaderUnmodified(headers, key);
|
var existing = GetHeaderUnmodified(headers, key);
|
||||||
if (existing == null)
|
SetHeaderUnmodified(headers, key, StringValues.Concat(existing, values));
|
||||||
{
|
|
||||||
SetHeaderUnmodified(headers, key, values);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetHeaderUnmodified(headers, key, existing.Concat(values));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long? GetContentLength([NotNull] IHeaderDictionary headers)
|
public static long? GetContentLength([NotNull] IHeaderDictionary headers)
|
||||||
{
|
{
|
||||||
const NumberStyles styles = NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite;
|
const NumberStyles styles = NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite;
|
||||||
long value;
|
long value;
|
||||||
string rawValue = headers.Get(HeaderNames.ContentLength);
|
var rawValue = headers[HeaderNames.ContentLength];
|
||||||
if (!string.IsNullOrWhiteSpace(rawValue) &&
|
if (rawValue.Count == 1 &&
|
||||||
long.TryParse(rawValue, styles, CultureInfo.InvariantCulture, out value))
|
!string.IsNullOrWhiteSpace(rawValue[0]) &&
|
||||||
|
long.TryParse(rawValue[0], styles, CultureInfo.InvariantCulture, out value))
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -12,16 +13,18 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ReadableStringCollection : IReadableStringCollection
|
public class ReadableStringCollection : IReadableStringCollection
|
||||||
{
|
{
|
||||||
|
public static readonly IReadableStringCollection Empty = new ReadableStringCollection(new Dictionary<string, StringValues>(0));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new wrapper
|
/// Create a new wrapper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="store"></param>
|
/// <param name="store"></param>
|
||||||
public ReadableStringCollection([NotNull] IDictionary<string, string[]> store)
|
public ReadableStringCollection([NotNull] IDictionary<string, StringValues> store)
|
||||||
{
|
{
|
||||||
Store = store;
|
Store = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDictionary<string, string[]> Store { get; set; }
|
private IDictionary<string, StringValues> Store { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the number of elements contained in the collection.
|
/// Gets the number of elements contained in the collection.
|
||||||
|
|
@ -42,13 +45,21 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the associated value from the collection. Multiple values will be merged.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -61,35 +72,12 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
return Store.ContainsKey(key);
|
return Store.ContainsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the associated value from the collection. Multiple values will be merged.
|
|
||||||
/// Returns null if the key is not present.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public string Get(string key)
|
|
||||||
{
|
|
||||||
return GetJoinedValue(Store, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the associated values from the collection in their original format.
|
|
||||||
/// Returns null if the key is not present.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public IList<string> GetValues(string key)
|
|
||||||
{
|
|
||||||
string[] values;
|
|
||||||
Store.TryGetValue(key, out values);
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
|
public IEnumerator<KeyValuePair<string, StringValues>> GetEnumerator()
|
||||||
{
|
{
|
||||||
return Store.GetEnumerator();
|
return Store.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
@ -102,15 +90,5 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
return GetEnumerator();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetJoinedValue(IDictionary<string, string[]> store, string key)
|
|
||||||
{
|
|
||||||
string[] values;
|
|
||||||
if (store.TryGetValue(key, out values))
|
|
||||||
{
|
|
||||||
return string.Join(",", values);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
@ -17,7 +18,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
_dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
_dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string this[string key]
|
public StringValues this[string key]
|
||||||
{
|
{
|
||||||
get { return Get(key); }
|
get { return Get(key); }
|
||||||
}
|
}
|
||||||
|
|
@ -88,11 +89,11 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
|
public IEnumerator<KeyValuePair<string, StringValues>> GetEnumerator()
|
||||||
{
|
{
|
||||||
foreach (var pair in _dictionary)
|
foreach (var pair in _dictionary)
|
||||||
{
|
{
|
||||||
yield return new KeyValuePair<string, string[]>(pair.Key, new[] { pair.Value });
|
yield return new KeyValuePair<string, StringValues>(pair.Key, pair.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Microsoft.Framework.WebEncoders;
|
using Microsoft.Framework.WebEncoders;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -33,11 +33,14 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public void Append(string key, string value)
|
public void Append(string key, string value)
|
||||||
{
|
{
|
||||||
Headers.AppendValues(HeaderNames.SetCookie,
|
var setCookieHeaderValue = new SetCookieHeaderValue(
|
||||||
new SetCookieHeaderValue(
|
|
||||||
UrlEncoder.Default.UrlEncode(key),
|
UrlEncoder.Default.UrlEncode(key),
|
||||||
UrlEncoder.Default.UrlEncode(value))
|
UrlEncoder.Default.UrlEncode(value))
|
||||||
{ Path = "/" }.ToString());
|
{
|
||||||
|
Path = "/"
|
||||||
|
};
|
||||||
|
|
||||||
|
Headers[HeaderNames.SetCookie] = StringValues.Concat(Headers[HeaderNames.SetCookie], setCookieHeaderValue.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -48,17 +51,18 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <param name="options"></param>
|
/// <param name="options"></param>
|
||||||
public void Append(string key, string value, [NotNull] CookieOptions options)
|
public void Append(string key, string value, [NotNull] CookieOptions options)
|
||||||
{
|
{
|
||||||
Headers.AppendValues(HeaderNames.SetCookie,
|
var setCookieHeaderValue = new SetCookieHeaderValue(
|
||||||
new SetCookieHeaderValue(
|
|
||||||
UrlEncoder.Default.UrlEncode(key),
|
UrlEncoder.Default.UrlEncode(key),
|
||||||
UrlEncoder.Default.UrlEncode(value))
|
UrlEncoder.Default.UrlEncode(value))
|
||||||
{
|
{
|
||||||
Domain = options.Domain,
|
Domain = options.Domain,
|
||||||
Path = options.Path,
|
Path = options.Path,
|
||||||
Expires = options.Expires,
|
Expires = options.Expires,
|
||||||
Secure = options.Secure,
|
Secure = options.Secure,
|
||||||
HttpOnly = options.HttpOnly,
|
HttpOnly = options.HttpOnly,
|
||||||
}.ToString());
|
};
|
||||||
|
|
||||||
|
Headers[HeaderNames.SetCookie] = StringValues.Concat(Headers[HeaderNames.SetCookie], setCookieHeaderValue.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -70,15 +74,15 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
var encodedKeyPlusEquals = UrlEncoder.Default.UrlEncode(key) + "=";
|
var encodedKeyPlusEquals = UrlEncoder.Default.UrlEncode(key) + "=";
|
||||||
Func<string, bool> predicate = value => value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
|
Func<string, bool> predicate = value => value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var deleteCookies = new[] { encodedKeyPlusEquals + "; expires=Thu, 01-Jan-1970 00:00:00 GMT" };
|
StringValues deleteCookies = encodedKeyPlusEquals + "; expires=Thu, 01-Jan-1970 00:00:00 GMT";
|
||||||
IList<string> existingValues = Headers.GetValues(HeaderNames.SetCookie);
|
var existingValues = Headers[HeaderNames.SetCookie];
|
||||||
if (existingValues == null || existingValues.Count == 0)
|
if (StringValues.IsNullOrEmpty(existingValues))
|
||||||
{
|
{
|
||||||
Headers.SetValues(HeaderNames.SetCookie, deleteCookies);
|
Headers[HeaderNames.SetCookie] = deleteCookies;
|
||||||
}
|
}
|
||||||
else
|
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 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
rejectPredicate = value => value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
|
rejectPredicate = value => value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
IList<string> existingValues = Headers.GetValues(HeaderNames.SetCookie);
|
var existingValues = Headers[HeaderNames.SetCookie];
|
||||||
if (existingValues != null)
|
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
|
Append(key, string.Empty, new CookieOptions
|
||||||
|
|
|
||||||
|
|
@ -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.Framework.Primitives;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Owin
|
||||||
|
{
|
||||||
|
internal class DictionaryStringArrayWrapper : IDictionary<string, string[]>
|
||||||
|
{
|
||||||
|
public DictionaryStringArrayWrapper(IDictionary<string, StringValues> inner)
|
||||||
|
{
|
||||||
|
Inner = inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly IDictionary<string, StringValues> Inner;
|
||||||
|
|
||||||
|
private KeyValuePair<string, StringValues> Convert(KeyValuePair<string, string[]> item) => new KeyValuePair<string, StringValues>(item.Key, item.Value);
|
||||||
|
|
||||||
|
private KeyValuePair<string, string[]> Convert(KeyValuePair<string, StringValues> item) => new KeyValuePair<string, string[]>(item.Key, item.Value);
|
||||||
|
|
||||||
|
private StringValues Convert(string[] item) => item;
|
||||||
|
|
||||||
|
private string[] Convert(StringValues item) => item;
|
||||||
|
|
||||||
|
string[] IDictionary<string, string[]>.this[string key]
|
||||||
|
{
|
||||||
|
get { return Inner[key]; }
|
||||||
|
set { Inner[key] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
int ICollection<KeyValuePair<string, string[]>>.Count => Inner.Count;
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, string[]>>.IsReadOnly => Inner.IsReadOnly;
|
||||||
|
|
||||||
|
ICollection<string> IDictionary<string, string[]>.Keys => Inner.Keys;
|
||||||
|
|
||||||
|
ICollection<string[]> IDictionary<string, string[]>.Values => Inner.Values.Select(Convert).ToList();
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, string[]>>.Add(KeyValuePair<string, string[]> item) => Inner.Add(Convert(item));
|
||||||
|
|
||||||
|
void IDictionary<string, string[]>.Add(string key, string[] value) => Inner.Add(key, value);
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, string[]>>.Clear() => Inner.Clear();
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, string[]>>.Contains(KeyValuePair<string, string[]> item) => Inner.Contains(Convert(item));
|
||||||
|
|
||||||
|
bool IDictionary<string, string[]>.ContainsKey(string key) => Inner.ContainsKey(key);
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, string[]>>.CopyTo(KeyValuePair<string, string[]>[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
foreach(var kv in Inner)
|
||||||
|
{
|
||||||
|
array[arrayIndex++] = Convert(kv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator<KeyValuePair<string, string[]>> IEnumerable<KeyValuePair<string, string[]>>.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, string[]>>.Remove(KeyValuePair<string, string[]> item) => Inner.Remove(Convert(item));
|
||||||
|
|
||||||
|
bool IDictionary<string, string[]>.Remove(string key) => Inner.Remove(key);
|
||||||
|
|
||||||
|
bool IDictionary<string, string[]>.TryGetValue(string key, out string[] value)
|
||||||
|
{
|
||||||
|
StringValues temp;
|
||||||
|
if (Inner.TryGetValue(key, out temp))
|
||||||
|
{
|
||||||
|
value = temp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
value = default(StringValues);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.Framework.Primitives;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Owin
|
||||||
|
{
|
||||||
|
internal class DictionaryStringValuesWrapper : IDictionary<string, StringValues>
|
||||||
|
{
|
||||||
|
public DictionaryStringValuesWrapper(IDictionary<string, string[]> inner)
|
||||||
|
{
|
||||||
|
Inner = inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly IDictionary<string, string[]> Inner;
|
||||||
|
|
||||||
|
private KeyValuePair<string, StringValues> Convert(KeyValuePair<string, string[]> item) => new KeyValuePair<string, StringValues>(item.Key, item.Value);
|
||||||
|
|
||||||
|
private KeyValuePair<string, string[]> Convert(KeyValuePair<string, StringValues> item) => new KeyValuePair<string, string[]>(item.Key, item.Value);
|
||||||
|
|
||||||
|
private StringValues Convert(string[] item) => item;
|
||||||
|
|
||||||
|
private string[] Convert(StringValues item) => item;
|
||||||
|
|
||||||
|
StringValues IDictionary<string, StringValues>.this[string key]
|
||||||
|
{
|
||||||
|
get { return Inner[key]; }
|
||||||
|
set { Inner[key] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
int ICollection<KeyValuePair<string, StringValues>>.Count => Inner.Count;
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, StringValues>>.IsReadOnly => Inner.IsReadOnly;
|
||||||
|
|
||||||
|
ICollection<string> IDictionary<string, StringValues>.Keys => Inner.Keys;
|
||||||
|
|
||||||
|
ICollection<StringValues> IDictionary<string, StringValues>.Values => Inner.Values.Select(Convert).ToList();
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, StringValues>>.Add(KeyValuePair<string, StringValues> item) => Inner.Add(Convert(item));
|
||||||
|
|
||||||
|
void IDictionary<string, StringValues>.Add(string key, StringValues value) => Inner.Add(key, value);
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, StringValues>>.Clear() => Inner.Clear();
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, StringValues>>.Contains(KeyValuePair<string, StringValues> item) => Inner.Contains(Convert(item));
|
||||||
|
|
||||||
|
bool IDictionary<string, StringValues>.ContainsKey(string key) => Inner.ContainsKey(key);
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, StringValues>>.CopyTo(KeyValuePair<string, StringValues>[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
foreach (var kv in Inner)
|
||||||
|
{
|
||||||
|
array[arrayIndex++] = Convert(kv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator<KeyValuePair<string, StringValues>> IEnumerable<KeyValuePair<string, StringValues>>.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, StringValues>>.Remove(KeyValuePair<string, StringValues> item) => Inner.Remove(Convert(item));
|
||||||
|
|
||||||
|
bool IDictionary<string, StringValues>.Remove(string key) => Inner.Remove(key);
|
||||||
|
|
||||||
|
bool IDictionary<string, StringValues>.TryGetValue(string key, out StringValues value)
|
||||||
|
{
|
||||||
|
string[] temp;
|
||||||
|
if (Inner.TryGetValue(key, out temp))
|
||||||
|
{
|
||||||
|
value = temp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
value = default(StringValues);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -56,13 +56,13 @@ namespace Microsoft.AspNet.Owin
|
||||||
{ OwinConstants.RequestPath, new FeatureMap<IHttpRequestFeature>(feature => feature.Path, () => string.Empty, (feature, value) => feature.Path = Convert.ToString(value)) },
|
{ OwinConstants.RequestPath, new FeatureMap<IHttpRequestFeature>(feature => feature.Path, () => string.Empty, (feature, value) => feature.Path = Convert.ToString(value)) },
|
||||||
{ OwinConstants.RequestQueryString, new FeatureMap<IHttpRequestFeature>(feature => Utilities.RemoveQuestionMark(feature.QueryString), () => string.Empty,
|
{ OwinConstants.RequestQueryString, new FeatureMap<IHttpRequestFeature>(feature => Utilities.RemoveQuestionMark(feature.QueryString), () => string.Empty,
|
||||||
(feature, value) => feature.QueryString = Utilities.AddQuestionMark(Convert.ToString(value))) },
|
(feature, value) => feature.QueryString = Utilities.AddQuestionMark(Convert.ToString(value))) },
|
||||||
{ OwinConstants.RequestHeaders, new FeatureMap<IHttpRequestFeature>(feature => feature.Headers, (feature, value) => feature.Headers = (IDictionary<string, string[]>)value) },
|
{ OwinConstants.RequestHeaders, new FeatureMap<IHttpRequestFeature>(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeDictionaryStringValues((IDictionary<string, string[]>)value)) },
|
||||||
{ OwinConstants.RequestBody, new FeatureMap<IHttpRequestFeature>(feature => feature.Body, () => Stream.Null, (feature, value) => feature.Body = (Stream)value) },
|
{ OwinConstants.RequestBody, new FeatureMap<IHttpRequestFeature>(feature => feature.Body, () => Stream.Null, (feature, value) => feature.Body = (Stream)value) },
|
||||||
{ OwinConstants.RequestUser, new FeatureMap<IHttpAuthenticationFeature>(feature => feature.User, () => null, (feature, value) => feature.User = (ClaimsPrincipal)value) },
|
{ OwinConstants.RequestUser, new FeatureMap<IHttpAuthenticationFeature>(feature => feature.User, () => null, (feature, value) => feature.User = (ClaimsPrincipal)value) },
|
||||||
|
|
||||||
{ OwinConstants.ResponseStatusCode, new FeatureMap<IHttpResponseFeature>(feature => feature.StatusCode, () => 200, (feature, value) => feature.StatusCode = Convert.ToInt32(value)) },
|
{ OwinConstants.ResponseStatusCode, new FeatureMap<IHttpResponseFeature>(feature => feature.StatusCode, () => 200, (feature, value) => feature.StatusCode = Convert.ToInt32(value)) },
|
||||||
{ OwinConstants.ResponseReasonPhrase, new FeatureMap<IHttpResponseFeature>(feature => feature.ReasonPhrase, (feature, value) => feature.ReasonPhrase = Convert.ToString(value)) },
|
{ OwinConstants.ResponseReasonPhrase, new FeatureMap<IHttpResponseFeature>(feature => feature.ReasonPhrase, (feature, value) => feature.ReasonPhrase = Convert.ToString(value)) },
|
||||||
{ OwinConstants.ResponseHeaders, new FeatureMap<IHttpResponseFeature>(feature => feature.Headers, (feature, value) => feature.Headers = (IDictionary<string, string[]>)value) },
|
{ OwinConstants.ResponseHeaders, new FeatureMap<IHttpResponseFeature>(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeDictionaryStringValues((IDictionary<string, string[]>)value)) },
|
||||||
{ OwinConstants.ResponseBody, new FeatureMap<IHttpResponseFeature>(feature => feature.Body, () => Stream.Null, (feature, value) => feature.Body = (Stream)value) },
|
{ OwinConstants.ResponseBody, new FeatureMap<IHttpResponseFeature>(feature => feature.Body, () => Stream.Null, (feature, value) => feature.Body = (Stream)value) },
|
||||||
{ OwinConstants.CommonKeys.OnSendingHeaders, new FeatureMap<IHttpResponseFeature>(
|
{ OwinConstants.CommonKeys.OnSendingHeaders, new FeatureMap<IHttpResponseFeature>(
|
||||||
feature => new Action<Action<object>, object>((cb, state) => {
|
feature => new Action<Action<object>, object>((cb, state) => {
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,10 @@ using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Features;
|
using Microsoft.AspNet.Http.Features;
|
||||||
using Microsoft.AspNet.Http.Features.Authentication;
|
using Microsoft.AspNet.Http.Features.Authentication;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Owin
|
namespace Microsoft.AspNet.Owin
|
||||||
{
|
{
|
||||||
|
|
@ -104,10 +106,10 @@ namespace Microsoft.AspNet.Owin
|
||||||
set { Prop(OwinConstants.RequestQueryString, Utilities.RemoveQuestionMark(value)); }
|
set { Prop(OwinConstants.RequestQueryString, Utilities.RemoveQuestionMark(value)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
IDictionary<string, string[]> IHttpRequestFeature.Headers
|
IDictionary<string, StringValues> IHttpRequestFeature.Headers
|
||||||
{
|
{
|
||||||
get { return Prop<IDictionary<string, string[]>>(OwinConstants.RequestHeaders); }
|
get { return Utilities.MakeDictionaryStringValues(Prop<IDictionary<string, string[]>>(OwinConstants.RequestHeaders)); }
|
||||||
set { Prop(OwinConstants.RequestHeaders, value); }
|
set { Prop(OwinConstants.RequestHeaders, Utilities.MakeDictionaryStringArray(value)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
string IHttpRequestIdentifierFeature.TraceIdentifier
|
string IHttpRequestIdentifierFeature.TraceIdentifier
|
||||||
|
|
@ -134,10 +136,10 @@ namespace Microsoft.AspNet.Owin
|
||||||
set { Prop(OwinConstants.ResponseReasonPhrase, value); }
|
set { Prop(OwinConstants.ResponseReasonPhrase, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
IDictionary<string, string[]> IHttpResponseFeature.Headers
|
IDictionary<string, StringValues> IHttpResponseFeature.Headers
|
||||||
{
|
{
|
||||||
get { return Prop<IDictionary<string, string[]>>(OwinConstants.ResponseHeaders); }
|
get { return Utilities.MakeDictionaryStringValues(Prop<IDictionary<string, string[]>>(OwinConstants.ResponseHeaders)); }
|
||||||
set { Prop(OwinConstants.ResponseHeaders, value); }
|
set { Prop(OwinConstants.ResponseHeaders, Utilities.MakeDictionaryStringArray(value)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream IHttpResponseFeature.Body
|
Stream IHttpResponseFeature.Body
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Claims;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Owin
|
namespace Microsoft.AspNet.Owin
|
||||||
{
|
{
|
||||||
|
|
@ -41,5 +45,25 @@ namespace Microsoft.AspNet.Owin
|
||||||
}
|
}
|
||||||
return new ClaimsPrincipal(principal);
|
return new ClaimsPrincipal(principal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static IDictionary<string, StringValues> MakeDictionaryStringValues(IDictionary<string, string[]> dictionary)
|
||||||
|
{
|
||||||
|
var wrapper = dictionary as DictionaryStringArrayWrapper;
|
||||||
|
if (wrapper != null)
|
||||||
|
{
|
||||||
|
return wrapper.Inner;
|
||||||
|
}
|
||||||
|
return new DictionaryStringValuesWrapper(dictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IDictionary<string, string[]> MakeDictionaryStringArray(IDictionary<string, StringValues> dictionary)
|
||||||
|
{
|
||||||
|
var wrapper = dictionary as DictionaryStringValuesWrapper;
|
||||||
|
if (wrapper != null)
|
||||||
|
{
|
||||||
|
return wrapper.Inner;
|
||||||
|
}
|
||||||
|
return new DictionaryStringArrayWrapper(dictionary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebUtilities
|
namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
|
|
@ -150,11 +151,11 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text">The HTTP form body to parse.</param>
|
/// <param name="text">The HTTP form body to parse.</param>
|
||||||
/// <returns>The collection containing the parsed HTTP form body.</returns>
|
/// <returns>The collection containing the parsed HTTP form body.</returns>
|
||||||
public static IDictionary<string, string[]> ReadForm(string text)
|
public static IDictionary<string, StringValues> ReadForm(string text)
|
||||||
{
|
{
|
||||||
var reader = new FormReader(text);
|
var reader = new FormReader(text);
|
||||||
|
|
||||||
var accumulator = new KeyValueAccumulator<string, string>(StringComparer.OrdinalIgnoreCase);
|
var accumulator = new KeyValueAccumulator();
|
||||||
var pair = reader.ReadNextPair();
|
var pair = reader.ReadNextPair();
|
||||||
while (pair.HasValue)
|
while (pair.HasValue)
|
||||||
{
|
{
|
||||||
|
|
@ -170,7 +171,7 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The HTTP form body to parse.</param>
|
/// <param name="stream">The HTTP form body to parse.</param>
|
||||||
/// <returns>The collection containing the parsed HTTP form body.</returns>
|
/// <returns>The collection containing the parsed HTTP form body.</returns>
|
||||||
public static Task<IDictionary<string, string[]>> ReadFormAsync(Stream stream, CancellationToken cancellationToken = new CancellationToken())
|
public static Task<IDictionary<string, StringValues>> ReadFormAsync(Stream stream, CancellationToken cancellationToken = new CancellationToken())
|
||||||
{
|
{
|
||||||
return ReadFormAsync(stream, Encoding.UTF8, cancellationToken);
|
return ReadFormAsync(stream, Encoding.UTF8, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -180,11 +181,11 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The HTTP form body to parse.</param>
|
/// <param name="stream">The HTTP form body to parse.</param>
|
||||||
/// <returns>The collection containing the parsed HTTP form body.</returns>
|
/// <returns>The collection containing the parsed HTTP form body.</returns>
|
||||||
public static async Task<IDictionary<string, string[]>> ReadFormAsync(Stream stream, Encoding encoding, CancellationToken cancellationToken = new CancellationToken())
|
public static async Task<IDictionary<string, StringValues>> ReadFormAsync(Stream stream, Encoding encoding, CancellationToken cancellationToken = new CancellationToken())
|
||||||
{
|
{
|
||||||
var reader = new FormReader(stream, encoding);
|
var reader = new FormReader(stream, encoding);
|
||||||
|
|
||||||
var accumulator = new KeyValueAccumulator<string, string>(StringComparer.OrdinalIgnoreCase);
|
var accumulator = new KeyValueAccumulator();
|
||||||
var pair = await reader.ReadNextPairAsync(cancellationToken);
|
var pair = await reader.ReadNextPairAsync(cancellationToken);
|
||||||
while (pair.HasValue)
|
while (pair.HasValue)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,37 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Collections.Generic;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebUtilities
|
namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
public class KeyValueAccumulator<TKey, TValue>
|
public class KeyValueAccumulator
|
||||||
{
|
{
|
||||||
private Dictionary<TKey, List<TValue>> _accumulator;
|
private Dictionary<string, List<string>> _accumulator;
|
||||||
IEqualityComparer<TKey> _comparer;
|
|
||||||
|
|
||||||
public KeyValueAccumulator([NotNull] IEqualityComparer<TKey> comparer)
|
public KeyValueAccumulator()
|
||||||
{
|
{
|
||||||
_comparer = comparer;
|
_accumulator = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
_accumulator = new Dictionary<TKey, List<TValue>>(comparer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Append(TKey key, TValue value)
|
public void Append(string key, string value)
|
||||||
{
|
{
|
||||||
List<TValue> values;
|
List<string> values;
|
||||||
if (_accumulator.TryGetValue(key, out values))
|
if (_accumulator.TryGetValue(key, out values))
|
||||||
{
|
{
|
||||||
values.Add(value);
|
values.Add(value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_accumulator[key] = new List<TValue>(1) { value };
|
_accumulator[key] = new List<string>(1) { value };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDictionary<TKey, TValue[]> GetResults()
|
public IDictionary<string, StringValues> GetResults()
|
||||||
{
|
{
|
||||||
var results = new Dictionary<TKey, TValue[]>(_comparer);
|
var results = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (var kv in _accumulator)
|
foreach (var kv in _accumulator)
|
||||||
{
|
{
|
||||||
results.Add(kv.Key, kv.Value.ToArray());
|
results.Add(kv.Key, kv.Value.ToArray());
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebUtilities
|
namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
|
|
@ -64,10 +65,10 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
return new MultipartSection() { Headers = headers, Body = _currentStream, BaseStreamOffset = baseStreamOffset };
|
return new MultipartSection() { Headers = headers, Body = _currentStream, BaseStreamOffset = baseStreamOffset };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IDictionary<string, string[]>> ReadHeadersAsync(CancellationToken cancellationToken)
|
private async Task<IDictionary<string, StringValues>> ReadHeadersAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
int totalSize = 0;
|
int totalSize = 0;
|
||||||
var accumulator = new KeyValueAccumulator<string, string>(StringComparer.OrdinalIgnoreCase);
|
var accumulator = new KeyValueAccumulator();
|
||||||
var line = await _stream.ReadLineAsync(HeaderLengthLimit, cancellationToken);
|
var line = await _stream.ReadLineAsync(HeaderLengthLimit, cancellationToken);
|
||||||
while (!string.IsNullOrEmpty(line))
|
while (!string.IsNullOrEmpty(line))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebUtilities
|
namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
|
|
@ -12,10 +13,10 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string[] values;
|
StringValues values;
|
||||||
if (Headers.TryGetValue("Content-Type", out values))
|
if (Headers.TryGetValue("Content-Type", out values))
|
||||||
{
|
{
|
||||||
return string.Join(", ", values);
|
return values;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -25,16 +26,16 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string[] values;
|
StringValues values;
|
||||||
if (Headers.TryGetValue("Content-Disposition", out values))
|
if (Headers.TryGetValue("Content-Disposition", out values))
|
||||||
{
|
{
|
||||||
return string.Join(", ", values);
|
return values;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDictionary<string, string[]> Headers { get; set; }
|
public IDictionary<string, StringValues> Headers { get; set; }
|
||||||
|
|
||||||
public Stream Body { get; set; }
|
public Stream Body { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Microsoft.Framework.WebEncoders;
|
using Microsoft.Framework.WebEncoders;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebUtilities
|
namespace Microsoft.AspNet.WebUtilities
|
||||||
|
|
@ -72,13 +73,13 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text">The raw query string value, with or without the leading '?'.</param>
|
/// <param name="text">The raw query string value, with or without the leading '?'.</param>
|
||||||
/// <returns>A collection of parsed keys and values.</returns>
|
/// <returns>A collection of parsed keys and values.</returns>
|
||||||
public static IDictionary<string, string[]> ParseQuery(string queryString)
|
public static IDictionary<string, StringValues> ParseQuery(string queryString)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(queryString) && queryString[0] == '?')
|
if (!string.IsNullOrEmpty(queryString) && queryString[0] == '?')
|
||||||
{
|
{
|
||||||
queryString = queryString.Substring(1);
|
queryString = queryString.Substring(1);
|
||||||
}
|
}
|
||||||
var accumulator = new KeyValueAccumulator<string, string>(StringComparer.OrdinalIgnoreCase);
|
var accumulator = new KeyValueAccumulator();
|
||||||
|
|
||||||
int textLength = queryString.Length;
|
int textLength = queryString.Length;
|
||||||
int equalIndex = queryString.IndexOf('=');
|
int equalIndex = queryString.IndexOf('=');
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,13 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" },
|
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" },
|
||||||
|
"Microsoft.Framework.Primitives": "1.0.0-*",
|
||||||
"Microsoft.Framework.WebEncoders.Core": "1.0.0-*"
|
"Microsoft.Framework.WebEncoders.Core": "1.0.0-*"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"dnx451": { },
|
"dnx451": { },
|
||||||
"dnxcore50": {
|
"dnxcore50": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"System.Collections": "4.0.11-beta-*",
|
|
||||||
"System.Diagnostics.Debug": "4.0.11-beta-*",
|
"System.Diagnostics.Debug": "4.0.11-beta-*",
|
||||||
"System.IO": "4.0.11-beta-*",
|
"System.IO": "4.0.11-beta-*",
|
||||||
"System.IO.FileSystem": "4.0.1-beta-*",
|
"System.IO.FileSystem": "4.0.1-beta-*",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>e5faccd4-6327-43aa-80a9-ae6f4a3bfe6a</ProjectGuid>
|
||||||
|
<RootNamespace>Microsoft.AspNet.Primitives</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,233 @@
|
||||||
|
// 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.Framework.Primitives
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents zero/null, one, or many strings in an efficient way.
|
||||||
|
/// </summary>
|
||||||
|
public struct StringValues : IList<string>
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return value.GetArrayValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => _values?.Length ?? (_value != null ? 1 : 0);
|
||||||
|
|
||||||
|
bool ICollection<string>.IsReadOnly
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
string IList<string>.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] ToArray()
|
||||||
|
{
|
||||||
|
return GetArrayValue() ?? EmptyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] GetArrayValue()
|
||||||
|
{
|
||||||
|
if (_value != null)
|
||||||
|
{
|
||||||
|
return new[] { _value };
|
||||||
|
}
|
||||||
|
return _values;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IList<string>.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<string>.Contains(string item)
|
||||||
|
{
|
||||||
|
return ((IList<string>)this).IndexOf(item) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<string>.CopyTo(string[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
array[arrayIndex + i] = this[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<string>.Add(string item)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList<string>.Insert(int index, string item)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection<string>.Remove(string item)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IList<string>.RemoveAt(int index)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<string>.Clear()
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return ((IEnumerable<string>)this).GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator<string> IEnumerable<string>.GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Count == 0)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"description": "Contains primitive types such as StringValues.",
|
||||||
|
"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-*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Microsoft.AspNet.Http.Features;
|
using Microsoft.AspNet.Http.Features;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
@ -64,9 +65,9 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
const string expected = "localhost:9001";
|
const string expected = "localhost:9001";
|
||||||
|
|
||||||
var headers = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
|
var headers = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
{ "Host", new string[] { expected } },
|
{ "Host", expected },
|
||||||
};
|
};
|
||||||
|
|
||||||
var request = CreateRequest(headers);
|
var request = CreateRequest(headers);
|
||||||
|
|
@ -84,9 +85,9 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
const string expected = "löcalhöst";
|
const string expected = "löcalhöst";
|
||||||
|
|
||||||
var headers = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
|
var headers = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
{ "Host", new string[]{ "xn--lcalhst-90ae" } },
|
{ "Host", "xn--lcalhst-90ae" },
|
||||||
};
|
};
|
||||||
|
|
||||||
var request = CreateRequest(headers);
|
var request = CreateRequest(headers);
|
||||||
|
|
@ -104,7 +105,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
const string expected = "xn--lcalhst-90ae";
|
const string expected = "xn--lcalhst-90ae";
|
||||||
|
|
||||||
var headers = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
var headers = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var request = CreateRequest(headers);
|
var request = CreateRequest(headers);
|
||||||
|
|
||||||
|
|
@ -149,9 +150,9 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
Assert.Equal("value0", query1["name0"]);
|
Assert.Equal("value0", query1["name0"]);
|
||||||
Assert.Equal("value1", query1["name1"]);
|
Assert.Equal("value1", query1["name1"]);
|
||||||
|
|
||||||
var query2 = new ReadableStringCollection(new Dictionary<string, string[]>()
|
var query2 = new ReadableStringCollection(new Dictionary<string, StringValues>()
|
||||||
{
|
{
|
||||||
{ "name2", new[] { "value2" } }
|
{ "name2", "value2" }
|
||||||
});
|
});
|
||||||
|
|
||||||
request.Query = query2;
|
request.Query = query2;
|
||||||
|
|
@ -164,30 +165,30 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
public void Cookies_GetAndSet()
|
public void Cookies_GetAndSet()
|
||||||
{
|
{
|
||||||
var request = new DefaultHttpContext().Request;
|
var request = new DefaultHttpContext().Request;
|
||||||
var cookieHeaders = request.Headers.GetValues("Cookie");
|
var cookieHeaders = request.Headers["Cookie"];
|
||||||
Assert.Null(cookieHeaders);
|
Assert.Equal(0, cookieHeaders.Count);
|
||||||
var cookies0 = request.Cookies;
|
var cookies0 = request.Cookies;
|
||||||
Assert.Equal(0, cookies0.Count);
|
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;
|
var cookies1 = request.Cookies;
|
||||||
Assert.Same(cookies0, cookies1);
|
Assert.Same(cookies0, cookies1);
|
||||||
Assert.Equal(2, cookies1.Count);
|
Assert.Equal(2, cookies1.Count);
|
||||||
Assert.Equal("value0", cookies1["name0"]);
|
Assert.Equal("value0", cookies1["name0"]);
|
||||||
Assert.Equal("value1", cookies1["name1"]);
|
Assert.Equal("value1", cookies1["name1"]);
|
||||||
|
|
||||||
var cookies2 = new ReadableStringCollection(new Dictionary<string, string[]>()
|
var cookies2 = new ReadableStringCollection(new Dictionary<string, StringValues>()
|
||||||
{
|
{
|
||||||
{ "name2", new[] { "value2" } }
|
{ "name2", "value2" }
|
||||||
});
|
});
|
||||||
request.Cookies = cookies2;
|
request.Cookies = cookies2;
|
||||||
Assert.Same(cookies2, request.Cookies);
|
Assert.Same(cookies2, request.Cookies);
|
||||||
Assert.Equal("value2", request.Cookies["name2"]);
|
Assert.Equal("value2", request.Cookies["name2"]);
|
||||||
cookieHeaders = request.Headers.GetValues("Cookie");
|
cookieHeaders = request.Headers["Cookie"];
|
||||||
Assert.Equal(new[] { "name2=value2" }, cookieHeaders);
|
Assert.Equal(new[] { "name2=value2" }, cookieHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpRequest CreateRequest(IDictionary<string, string[]> headers)
|
private static HttpRequest CreateRequest(IDictionary<string, StringValues> headers)
|
||||||
{
|
{
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
context.GetFeature<IHttpRequestFeature>().Headers = headers;
|
context.GetFeature<IHttpRequestFeature>().Headers = headers;
|
||||||
|
|
@ -216,10 +217,10 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
private static HttpRequest GetRequestWithHeader(string headerName, string headerValue)
|
private static HttpRequest GetRequestWithHeader(string headerName, string headerValue)
|
||||||
{
|
{
|
||||||
var headers = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
var headers = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
|
||||||
if (headerValue != null)
|
if (headerValue != null)
|
||||||
{
|
{
|
||||||
headers.Add(headerName, new[] { headerValue });
|
headers.Add(headerName, headerValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CreateRequest(headers);
|
return CreateRequest(headers);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Framework.Primitives;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
@ -13,9 +14,9 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
public void PropertiesAreAccessible()
|
public void PropertiesAreAccessible()
|
||||||
{
|
{
|
||||||
var headers = new HeaderDictionary(
|
var headers = new HeaderDictionary(
|
||||||
new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
|
new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
{ "Header1", new[] { "Value1" } }
|
{ "Header1", "Value1" }
|
||||||
});
|
});
|
||||||
|
|
||||||
Assert.Equal(1, headers.Count);
|
Assert.Equal(1, headers.Count);
|
||||||
|
|
@ -23,8 +24,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
Assert.True(headers.ContainsKey("header1"));
|
Assert.True(headers.ContainsKey("header1"));
|
||||||
Assert.False(headers.ContainsKey("header2"));
|
Assert.False(headers.ContainsKey("header2"));
|
||||||
Assert.Equal("Value1", headers["header1"]);
|
Assert.Equal("Value1", headers["header1"]);
|
||||||
Assert.Equal("Value1", headers.Get("header1"));
|
Assert.Equal(new[] { "Value1" }, headers["header1"].ToArray());
|
||||||
Assert.Equal(new[] { "Value1" }, headers.GetValues("header1"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>61f72e92-b3ae-4a10-b838-44f80aed40ae</ProjectGuid>
|
||||||
|
<RootNamespace>Microsoft.AspNet.Primitives.Tests</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
// 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.Framework.Primitives
|
||||||
|
{
|
||||||
|
public class StringValuesTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IsReadOnly_True()
|
||||||
|
{
|
||||||
|
var stringValues = new StringValues();
|
||||||
|
Assert.True(((IList<string>)stringValues).IsReadOnly);
|
||||||
|
Assert.Throws<NotSupportedException>(() => ((IList<string>)stringValues)[0] = string.Empty);
|
||||||
|
Assert.Throws<NotSupportedException>(() => ((ICollection<string>)stringValues).Add(string.Empty));
|
||||||
|
Assert.Throws<NotSupportedException>(() => ((IList<string>)stringValues).Insert(0, string.Empty));
|
||||||
|
Assert.Throws<NotSupportedException>(() => ((ICollection<string>)stringValues).Remove(string.Empty));
|
||||||
|
Assert.Throws<NotSupportedException>(() => ((IList<string>)stringValues).RemoveAt(0));
|
||||||
|
Assert.Throws<NotSupportedException>(() => ((ICollection<string>)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.ToArray());
|
||||||
|
|
||||||
|
Assert.True(StringValues.IsNullOrEmpty(stringValues));
|
||||||
|
Assert.Throws<IndexOutOfRangeException>(() => stringValues[0]);
|
||||||
|
Assert.Equal(string.Empty, stringValues.ToString());
|
||||||
|
Assert.Equal(-1, ((IList<string>)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.Null((string)stringValues);
|
||||||
|
Assert.Equal(string.Empty, stringValues.ToString());
|
||||||
|
Assert.Null((string[])stringValues);
|
||||||
|
Assert.Equal(new string[0], stringValues.ToArray());
|
||||||
|
|
||||||
|
Assert.True(StringValues.IsNullOrEmpty(stringValues));
|
||||||
|
Assert.Throws<IndexOutOfRangeException>(() => stringValues[0]);
|
||||||
|
Assert.Equal(-1, ((IList<string>)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.Null((string)stringValues);
|
||||||
|
Assert.Equal(string.Empty, stringValues.ToString());
|
||||||
|
Assert.Null((string[])stringValues);
|
||||||
|
Assert.Equal(new string[0], stringValues.ToArray());
|
||||||
|
|
||||||
|
Assert.True(StringValues.IsNullOrEmpty(stringValues));
|
||||||
|
Assert.Throws<IndexOutOfRangeException>(() => stringValues[0]);
|
||||||
|
Assert.Equal(string.Empty, stringValues.ToString());
|
||||||
|
Assert.Equal(-1, ((IList<string>)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.Null((string)stringValues);
|
||||||
|
Assert.Null((string[])stringValues);
|
||||||
|
|
||||||
|
string aString = "abc";
|
||||||
|
stringValues = aString;
|
||||||
|
Assert.Equal(1, stringValues.Count);
|
||||||
|
Assert.Equal(aString, stringValues);
|
||||||
|
Assert.Equal(aString, stringValues[0]);
|
||||||
|
Assert.Equal<string[]>(new string[] { aString }, stringValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ImplicitStringArrayConverter_Works()
|
||||||
|
{
|
||||||
|
string[] nullStringArray = null;
|
||||||
|
StringValues stringValues = nullStringArray;
|
||||||
|
Assert.Equal(0, stringValues.Count);
|
||||||
|
Assert.Null((string)stringValues);
|
||||||
|
Assert.Null((string[])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<string[]>(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<string[]>(aStringArray, stringValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Framework.Primitives": "1.0.0-*",
|
||||||
|
"xunit.runner.aspnet": "2.0.0-aspnet-*"
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"test": "xunit.runner.aspnet"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"dnx451": { },
|
||||||
|
"dnxcore50": { }
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue