#426 Less alloc/wrapping/boxing for Query, Forms, Cookies
This commit is contained in:
parent
af0d2e5888
commit
3c2e2b9d98
|
|
@ -100,7 +100,7 @@ namespace Microsoft.AspNet.Http
|
||||||
string fragmentValue = uri.GetComponents(UriComponents.Fragment, UriFormat.UriEscaped);
|
string fragmentValue = uri.GetComponents(UriComponents.Fragment, UriFormat.UriEscaped);
|
||||||
if (!string.IsNullOrEmpty(fragmentValue))
|
if (!string.IsNullOrEmpty(fragmentValue))
|
||||||
{
|
{
|
||||||
fragmentValue = "#" + fragmentValue;
|
fragmentValue = $"#{fragmentValue}";
|
||||||
}
|
}
|
||||||
return new FragmentString(fragmentValue);
|
return new FragmentString(fragmentValue);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,18 +70,18 @@ namespace Microsoft.AspNet.Http
|
||||||
&& _value.IndexOf(':', index + 1) >= 0)
|
&& _value.IndexOf(':', index + 1) >= 0)
|
||||||
{
|
{
|
||||||
// IPv6 without brackets ::1 is the only type of host with 2 or more colons
|
// IPv6 without brackets ::1 is the only type of host with 2 or more colons
|
||||||
return "[" + _value + "]";
|
return $"[{_value}]";
|
||||||
}
|
}
|
||||||
else if (index >= 0)
|
else if (index >= 0)
|
||||||
{
|
{
|
||||||
// Has a port
|
// Has a port
|
||||||
string port = _value.Substring(index);
|
string port = _value.Substring(index);
|
||||||
IdnMapping mapping = new IdnMapping();
|
var mapping = new IdnMapping();
|
||||||
return mapping.GetAscii(_value, 0, index) + port;
|
return mapping.GetAscii(_value, 0, index) + port;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IdnMapping mapping = new IdnMapping();
|
var mapping = new IdnMapping();
|
||||||
return mapping.GetAscii(_value);
|
return mapping.GetAscii(_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -115,12 +115,12 @@ namespace Microsoft.AspNet.Http
|
||||||
{
|
{
|
||||||
// Has a port
|
// Has a port
|
||||||
string port = uriComponent.Substring(index);
|
string port = uriComponent.Substring(index);
|
||||||
IdnMapping mapping = new IdnMapping();
|
var mapping = new IdnMapping();
|
||||||
uriComponent = mapping.GetUnicode(uriComponent, 0, index) + port;
|
uriComponent = mapping.GetUnicode(uriComponent, 0, index) + port;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IdnMapping mapping = new IdnMapping();
|
var mapping = new IdnMapping();
|
||||||
uriComponent = mapping.GetUnicode(uriComponent);
|
uriComponent = mapping.GetUnicode(uriComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,13 @@ namespace Microsoft.AspNet.Http
|
||||||
public abstract string Method { get; set; }
|
public abstract string Method { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or set the HTTP request scheme from owin.RequestScheme.
|
/// Gets or set the HTTP request scheme.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The HTTP request scheme from owin.RequestScheme.</returns>
|
/// <returns>The HTTP request scheme.</returns>
|
||||||
public abstract string Scheme { get; set; }
|
public abstract string Scheme { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the owin.RequestScheme is https.
|
/// Returns true if the RequestScheme is https.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>true if this request is using https; otherwise, false.</returns>
|
/// <returns>true if this request is using https; otherwise, false.</returns>
|
||||||
public abstract bool IsHttps { get; set; }
|
public abstract bool IsHttps { get; set; }
|
||||||
|
|
@ -42,33 +42,33 @@ namespace Microsoft.AspNet.Http
|
||||||
public abstract HostString Host { get; set; }
|
public abstract HostString Host { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or set the owin.RequestPathBase.
|
/// Gets or set the RequestPathBase.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The owin.RequestPathBase.</returns>
|
/// <returns>The RequestPathBase.</returns>
|
||||||
public abstract PathString PathBase { get; set; }
|
public abstract PathString PathBase { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or set the request path from owin.RequestPath.
|
/// Gets or set the request path from RequestPath.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The request path from owin.RequestPath.</returns>
|
/// <returns>The request path from RequestPath.</returns>
|
||||||
public abstract PathString Path { get; set; }
|
public abstract PathString Path { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or set the query string from owin.RequestQueryString.
|
/// Gets or set the query string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The query string from owin.RequestQueryString.</returns>
|
/// <returns>The query string.</returns>
|
||||||
public abstract QueryString QueryString { get; set; }
|
public abstract QueryString QueryString { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the query value collection parsed from owin.RequestQueryString.
|
/// Gets the query value collection parsed from RequestQueryString.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The query value collection parsed from owin.RequestQueryString.</returns>
|
/// <returns>The query value collection parsed from RequestQueryString.</returns>
|
||||||
public abstract IReadableStringCollection Query { get; set; }
|
public abstract IQueryCollection Query { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or set the owin.RequestProtocol.
|
/// Gets or set the RequestProtocol.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The owin.RequestProtocol.</returns>
|
/// <returns>The RequestProtocol.</returns>
|
||||||
public abstract string Protocol { get; set; }
|
public abstract string Protocol { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.AspNet.Http
|
||||||
/// Gets the collection of Cookies for this request.
|
/// Gets the collection of Cookies for this request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The collection of Cookies for this request.</returns>
|
/// <returns>The collection of Cookies for this request.</returns>
|
||||||
public abstract IReadableStringCollection Cookies { get; set; }
|
public abstract IRequestCookieCollection Cookies { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Content-Length header
|
/// Gets or sets the Content-Length header
|
||||||
|
|
@ -95,9 +95,9 @@ namespace Microsoft.AspNet.Http
|
||||||
public abstract string ContentType { get; set; }
|
public abstract string ContentType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or set the owin.RequestBody Stream.
|
/// Gets or set the RequestBody Stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The owin.RequestBody Stream.</returns>
|
/// <returns>The RequestBody Stream.</returns>
|
||||||
public abstract Stream Body { get; set; }
|
public abstract Stream Body { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,95 @@
|
||||||
// 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.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http
|
namespace Microsoft.AspNet.Http
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains the parsed form values.
|
/// Represents the parsed form values sent with the HttpRequest.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IFormCollection : IReadableStringCollection
|
public interface IFormCollection : IEnumerable<KeyValuePair<string, StringValues>>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of elements contained in the <see cref="T:Microsoft.AspNet.Http.IFormCollection" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// The number of elements contained in the <see cref="T:Microsoft.AspNet.Http.IFormCollection" />.
|
||||||
|
/// </returns>
|
||||||
|
int Count { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an <see cref="T:System.Collections.Generic.ICollection`1" /> containing the keys of the
|
||||||
|
/// <see cref="T:Microsoft.AspNet.Http.IFormCollection" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// An <see cref="T:System.Collections.Generic.ICollection`1" /> containing the keys of the object
|
||||||
|
/// that implements <see cref="T:Microsoft.AspNet.Http.IFormCollection" />.
|
||||||
|
/// </returns>
|
||||||
|
ICollection<string> Keys { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the <see cref="T:Microsoft.AspNet.Http.IFormCollection" /> contains an element
|
||||||
|
/// with the specified key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">
|
||||||
|
/// The key to locate in the <see cref="T:Microsoft.AspNet.Http.IFormCollection" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// true if the <see cref="T:Microsoft.AspNet.Http.IFormCollection" /> contains an element with
|
||||||
|
/// the key; otherwise, false.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// key is null.
|
||||||
|
/// </exception>
|
||||||
|
bool ContainsKey(string key);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value associated with the specified key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">
|
||||||
|
/// The key of the value to get.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="value">
|
||||||
|
/// The key of the value to get.
|
||||||
|
/// When this method returns, the value associated with the specified key, if the
|
||||||
|
/// key is found; otherwise, the default value for the type of the value parameter.
|
||||||
|
/// This parameter is passed uninitialized.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// true if the object that implements <see cref="T:Microsoft.AspNet.Http.IFormCollection" /> contains
|
||||||
|
/// an element with the specified key; otherwise, false.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// key is null.
|
||||||
|
/// </exception>
|
||||||
|
bool TryGetValue(string key, out StringValues value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value with the specified key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">
|
||||||
|
/// The key of the value to get.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The element with the specified key, or <see cref="T:Microsoft.Extensions.Primitives.StringValues" />.Empty if the key is not present.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// key is null.
|
||||||
|
/// </exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="T:Microsoft.AspNet.Http.IFormCollection" /> has a different indexer contract than
|
||||||
|
/// <see cref="T:System.Collections.Generic.IDictionary`2" />, as it will return StringValues.Empty for missing entries
|
||||||
|
/// rather than throwing an Exception.
|
||||||
|
/// </remarks>
|
||||||
|
StringValues this[string key] { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The file collection sent with the request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns>The files included with the request.</returns>
|
||||||
IFormFileCollection Files { get; }
|
IFormFileCollection Files { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ using System.IO;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http
|
namespace Microsoft.AspNet.Http
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a file sent with the HttpRequest.
|
||||||
|
/// </summary>
|
||||||
public interface IFormFile
|
public interface IFormFile
|
||||||
{
|
{
|
||||||
string ContentType { get; }
|
string ContentType { get; }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http
|
namespace Microsoft.AspNet.Http
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the collection of files sent with the HttpRequest.
|
||||||
|
/// </summary>
|
||||||
public interface IFormFileCollection : IReadOnlyList<IFormFile>
|
public interface IFormFileCollection : IReadOnlyList<IFormFile>
|
||||||
{
|
{
|
||||||
IFormFile this[string name] { get; }
|
IFormFile this[string name] { get; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Http
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the HttpRequest query string collection
|
||||||
|
/// </summary>
|
||||||
|
public interface IQueryCollection : IEnumerable<KeyValuePair<string, StringValues>>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of elements contained in the <see cref="T:Microsoft.AspNet.Http.IQueryCollection" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// The number of elements contained in the <see cref="T:Microsoft.AspNet.Http.IQueryCollection" />.
|
||||||
|
/// </returns>
|
||||||
|
int Count { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an <see cref="T:System.Collections.Generic.ICollection`1" /> containing the keys of the
|
||||||
|
/// <see cref="T:Microsoft.AspNet.Http.IQueryCollection" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// An <see cref="T:System.Collections.Generic.ICollection`1" /> containing the keys of the object
|
||||||
|
/// that implements <see cref="T:Microsoft.AspNet.Http.IQueryCollection" />.
|
||||||
|
/// </returns>
|
||||||
|
ICollection<string> Keys { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the <see cref="T:Microsoft.AspNet.Http.IQueryCollection" /> contains an element
|
||||||
|
/// with the specified key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">
|
||||||
|
/// The key to locate in the <see cref="T:Microsoft.AspNet.Http.IQueryCollection" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// true if the <see cref="T:Microsoft.AspNet.Http.IQueryCollection" /> contains an element with
|
||||||
|
/// the key; otherwise, false.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// key is null.
|
||||||
|
/// </exception>
|
||||||
|
bool ContainsKey(string key);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value associated with the specified key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">
|
||||||
|
/// The key of the value to get.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="value">
|
||||||
|
/// The key of the value to get.
|
||||||
|
/// When this method returns, the value associated with the specified key, if the
|
||||||
|
/// key is found; otherwise, the default value for the type of the value parameter.
|
||||||
|
/// This parameter is passed uninitialized.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// true if the object that implements <see cref="T:Microsoft.AspNet.Http.IQueryCollection" /> contains
|
||||||
|
/// an element with the specified key; otherwise, false.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// key is null.
|
||||||
|
/// </exception>
|
||||||
|
bool TryGetValue(string key, out StringValues value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value with the specified key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">
|
||||||
|
/// The key of the value to get.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The element with the specified key, or <see cref="T:Microsoft.Extensions.Primitives.StringValues" />.Empty if the key is not present.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// key is null.
|
||||||
|
/// </exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="T:Microsoft.AspNet.Http.IQueryCollection" /> has a different indexer contract than
|
||||||
|
/// <see cref="T:System.Collections.Generic.IDictionary`2" />, as it will return StringValues.Empty for missing entries
|
||||||
|
/// rather than throwing an Exception.
|
||||||
|
/// </remarks>
|
||||||
|
StringValues this[string key] { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Accessors for headers, query, forms, etc.
|
|
||||||
/// </summary>
|
|
||||||
public interface IReadableStringCollection : IEnumerable<KeyValuePair<string, StringValues>>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Get the associated value from the collection.
|
|
||||||
/// Returns StringValues.Empty if the key is not present.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
StringValues this[string key] { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of elements contained in the collection.
|
|
||||||
/// </summary>
|
|
||||||
int Count { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a collection containing the keys.
|
|
||||||
/// </summary>
|
|
||||||
ICollection<string> Keys { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the collection contains an element with the specified key.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
bool ContainsKey(string key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Http
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the HttpRequest cookie collection
|
||||||
|
/// </summary>
|
||||||
|
public interface IRequestCookieCollection : IEnumerable<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of elements contained in the <see cref="T:Microsoft.AspNet.Http.IRequestCookieCollection" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// The number of elements contained in the <see cref="T:Microsoft.AspNet.Http.IRequestCookieCollection" />.
|
||||||
|
/// </returns>
|
||||||
|
int Count { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an <see cref="T:System.Collections.Generic.ICollection`1" /> containing the keys of the
|
||||||
|
/// <see cref="T:Microsoft.AspNet.Http.IRequestCookieCollection" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// An <see cref="T:System.Collections.Generic.ICollection`1" /> containing the keys of the object
|
||||||
|
/// that implements <see cref="T:Microsoft.AspNet.Http.IRequestCookieCollection" />.
|
||||||
|
/// </returns>
|
||||||
|
ICollection<string> Keys { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the <see cref="T:Microsoft.AspNet.Http.IRequestCookieCollection" /> contains an element
|
||||||
|
/// with the specified key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">
|
||||||
|
/// The key to locate in the <see cref="T:Microsoft.AspNet.Http.IRequestCookieCollection" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// true if the <see cref="T:Microsoft.AspNet.Http.IRequestCookieCollection" /> contains an element with
|
||||||
|
/// the key; otherwise, false.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// key is null.
|
||||||
|
/// </exception>
|
||||||
|
bool ContainsKey(string key);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value associated with the specified key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">
|
||||||
|
/// The key of the value to get.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="value">
|
||||||
|
/// The key of the value to get.
|
||||||
|
/// When this method returns, the value associated with the specified key, if the
|
||||||
|
/// key is found; otherwise, the default value for the type of the value parameter.
|
||||||
|
/// This parameter is passed uninitialized.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// true if the object that implements <see cref="T:Microsoft.AspNet.Http.IRequestCookieCollection" /> contains
|
||||||
|
/// an element with the specified key; otherwise, false.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// key is null.
|
||||||
|
/// </exception>
|
||||||
|
bool TryGetValue(string key, out string value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value with the specified key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">
|
||||||
|
/// The key of the value to get.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The element with the specified key, or <see cref="T:System.String" />.Empty if the key is not present.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">
|
||||||
|
/// key is null.
|
||||||
|
/// </exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="T:Microsoft.AspNet.Http.IRequestCookieCollection" /> has a different indexer contract than
|
||||||
|
/// <see cref="T:System.Collections.Generic.IDictionary`2" />, as it will return String.Empty for missing entries
|
||||||
|
/// rather than throwing an Exception.
|
||||||
|
/// </remarks>
|
||||||
|
string this[string key] { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,8 @@ namespace Microsoft.AspNet.Http
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct PathString : IEquatable<PathString>
|
public struct PathString : IEquatable<PathString>
|
||||||
{
|
{
|
||||||
|
private static readonly char[] splitChar = { '/' };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the empty path. This field is read-only.
|
/// Represents the empty path. This field is read-only.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -66,7 +68,24 @@ namespace Microsoft.AspNet.Http
|
||||||
public string ToUriComponent()
|
public string ToUriComponent()
|
||||||
{
|
{
|
||||||
// TODO: Measure the cost of this escaping and consider optimizing.
|
// TODO: Measure the cost of this escaping and consider optimizing.
|
||||||
return HasValue ? string.Join("/", _value.Split('/').Select(UrlEncoder.Default.Encode)) : string.Empty;
|
if (!HasValue)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
var values = _value.Split(splitChar);
|
||||||
|
var changed = false;
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
var value = values[i];
|
||||||
|
values[i] = UrlEncoder.Default.Encode(value);
|
||||||
|
|
||||||
|
if (!changed && value != values[i])
|
||||||
|
{
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed ? string.Join("/", values) : _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ namespace Microsoft.AspNet.Http
|
||||||
throw new ArgumentNullException(nameof(value));
|
throw new ArgumentNullException(nameof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new QueryString("?" + UrlEncoder.Default.Encode(name) + '=' + UrlEncoder.Default.Encode(value));
|
return new QueryString($"?{UrlEncoder.Default.Encode(name)}={UrlEncoder.Default.Encode(value)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Http
|
||||||
/// <returns>the associated values from the collection separated into individual values, or StringValues.Empty 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 static string[] GetCommaSeparatedValues(this IHeaderDictionary headers, string key)
|
public static string[] GetCommaSeparatedValues(this IHeaderDictionary headers, string key)
|
||||||
{
|
{
|
||||||
return ParsingHelpers.GetHeaderSplit(headers, key);
|
return ParsingHelpers.GetHeaderSplit(headers, key).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,49 @@ namespace Microsoft.AspNet.Http
|
||||||
{
|
{
|
||||||
headers.Remove(name);
|
headers.Remove(name);
|
||||||
}
|
}
|
||||||
|
else if (values.Count == 1)
|
||||||
|
{
|
||||||
|
headers[name] = new StringValues(values[0].ToString());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
headers[name] = values.Select(value => value.ToString()).ToArray();
|
var newValues = new string[values.Count];
|
||||||
|
for (var i = 0; i < values.Count; i++)
|
||||||
|
{
|
||||||
|
newValues[i] = values[i].ToString();
|
||||||
|
}
|
||||||
|
headers[name] = new StringValues(newValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AppendList<T>(this IHeaderDictionary Headers, string name, IList<T> values)
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (values.Count)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
Headers.Append(name, StringValues.Empty);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
Headers.Append(name, new StringValues(values[0].ToString()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var newValues = new string[values.Count];
|
||||||
|
for (var i = 0; i < values.Count; i++)
|
||||||
|
{
|
||||||
|
newValues[i] = values[i].ToString();
|
||||||
|
}
|
||||||
|
Headers.Append(name, new StringValues(newValues));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,7 +179,7 @@ namespace Microsoft.AspNet.Http
|
||||||
if (KnownParsers.TryGetValue(typeof(T), out temp))
|
if (KnownParsers.TryGetValue(typeof(T), out temp))
|
||||||
{
|
{
|
||||||
var func = (Func<string, T>)temp;
|
var func = (Func<string, T>)temp;
|
||||||
return func(headers[name]);
|
return func(headers[name].ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = headers[name];
|
var value = headers[name];
|
||||||
|
|
@ -148,7 +188,7 @@ namespace Microsoft.AspNet.Http
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetViaReflection<T>(value);
|
return GetViaReflection<T>(value.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IList<T> GetList<T>(this IHeaderDictionary headers, string name)
|
internal static IList<T> GetList<T>(this IHeaderDictionary headers, string name)
|
||||||
|
|
@ -162,7 +202,7 @@ 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[name]);
|
return func(headers[name].ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
var values = headers[name];
|
var values = headers[name];
|
||||||
|
|
@ -179,7 +219,7 @@ namespace Microsoft.AspNet.Http
|
||||||
// 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);
|
||||||
var method = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
var method = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||||
.Where(methodInfo =>
|
.FirstOrDefault(methodInfo =>
|
||||||
{
|
{
|
||||||
if (string.Equals("TryParse", methodInfo.Name, StringComparison.Ordinal)
|
if (string.Equals("TryParse", methodInfo.Name, StringComparison.Ordinal)
|
||||||
&& methodInfo.ReturnParameter.ParameterType.Equals(typeof(bool)))
|
&& methodInfo.ReturnParameter.ParameterType.Equals(typeof(bool)))
|
||||||
|
|
@ -191,7 +231,7 @@ namespace Microsoft.AspNet.Http
|
||||||
&& methodParams[1].ParameterType.Equals(type.MakeByRefType());
|
&& methodParams[1].ParameterType.Equals(type.MakeByRefType());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}).FirstOrDefault();
|
});
|
||||||
|
|
||||||
if (method == null)
|
if (method == null)
|
||||||
{
|
{
|
||||||
|
|
@ -213,7 +253,7 @@ namespace Microsoft.AspNet.Http
|
||||||
// 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);
|
||||||
var method = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
var method = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||||
.Where(methodInfo =>
|
.FirstOrDefault(methodInfo =>
|
||||||
{
|
{
|
||||||
if (string.Equals("TryParseList", methodInfo.Name, StringComparison.Ordinal)
|
if (string.Equals("TryParseList", methodInfo.Name, StringComparison.Ordinal)
|
||||||
&& methodInfo.ReturnParameter.ParameterType.Equals(typeof(Boolean)))
|
&& methodInfo.ReturnParameter.ParameterType.Equals(typeof(Boolean)))
|
||||||
|
|
@ -225,7 +265,7 @@ namespace Microsoft.AspNet.Http
|
||||||
&& methodParams[1].ParameterType.Equals(typeof(IList<T>).MakeByRefType());
|
&& methodParams[1].ParameterType.Equals(typeof(IList<T>).MakeByRefType());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}).FirstOrDefault();
|
});
|
||||||
|
|
||||||
if (method == null)
|
if (method == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
using System;
|
// 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 Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// 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;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -16,7 +19,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
public bool Equals(HeaderSegmentCollection other)
|
public bool Equals(HeaderSegmentCollection other)
|
||||||
{
|
{
|
||||||
return Equals(_headers, other._headers);
|
return StringValues.Equals(_headers, other._headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
internal static class ParsingHelpers
|
internal static class ParsingHelpers
|
||||||
{
|
{
|
||||||
public static StringValues GetHeader(IDictionary<string, StringValues> headers, string key)
|
public static StringValues GetHeader(IHeaderDictionary headers, string key)
|
||||||
{
|
{
|
||||||
StringValues value;
|
StringValues value;
|
||||||
return headers.TryGetValue(key, out value) ? value : StringValues.Empty;
|
return headers.TryGetValue(key, out value) ? value : StringValues.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StringValues GetHeaderSplit(IDictionary<string, StringValues> headers, string key)
|
public static StringValues GetHeaderSplit(IHeaderDictionary headers, string key)
|
||||||
{
|
{
|
||||||
var values = GetHeaderUnmodified(headers, key);
|
var values = GetHeaderUnmodified(headers, key);
|
||||||
return new StringValues(GetHeaderSplitImplementation(values).ToArray());
|
return new StringValues(GetHeaderSplitImplementation(values).ToArray());
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StringValues GetHeaderUnmodified(IDictionary<string, StringValues> headers, string key)
|
public static StringValues GetHeaderUnmodified(IHeaderDictionary headers, string key)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
{
|
{
|
||||||
|
|
@ -44,7 +44,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
return headers.TryGetValue(key, out values) ? values : StringValues.Empty;
|
return headers.TryGetValue(key, out values) ? values : StringValues.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetHeaderJoined(IDictionary<string, StringValues> headers, string key, StringValues value)
|
public static void SetHeaderJoined(IHeaderDictionary headers, string key, StringValues value)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
{
|
{
|
||||||
|
|
@ -61,35 +61,26 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
headers[key] = string.Join(",", value.Select(QuoteIfNeeded));
|
headers[key] = string.Join(",", value.Select((s) => QuoteIfNeeded(s)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quote items that contain comas and are not already quoted.
|
// Quote items that contain comas and are not already quoted.
|
||||||
private static string QuoteIfNeeded(string value)
|
private static string QuoteIfNeeded(string value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (!string.IsNullOrWhiteSpace(value) &&
|
||||||
{
|
value.Contains(',') &&
|
||||||
// Ignore
|
(value[0] != '"' || value[value.Length - 1] != '"'))
|
||||||
|
{
|
||||||
|
return $"\"{value}\"";
|
||||||
}
|
}
|
||||||
else if (value.Contains(','))
|
|
||||||
{
|
|
||||||
if (value[0] != '"' || value[value.Length - 1] != '"')
|
|
||||||
{
|
|
||||||
value = '"' + value + '"';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string DeQuote(string value)
|
private static string DeQuote(string value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (!string.IsNullOrWhiteSpace(value) &&
|
||||||
{
|
(value.Length > 1 && value[0] == '"' && value[value.Length - 1] == '"'))
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
else if (value.Length > 1 && value[0] == '"' && value[value.Length - 1] == '"')
|
|
||||||
{
|
{
|
||||||
value = value.Substring(1, value.Length - 2);
|
value = value.Substring(1, value.Length - 2);
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +88,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetHeaderUnmodified(IDictionary<string, StringValues> headers, string key, StringValues? values)
|
public static void SetHeaderUnmodified(IHeaderDictionary headers, string key, StringValues? values)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
{
|
{
|
||||||
|
|
@ -118,7 +109,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AppendHeaderJoined(IDictionary<string, StringValues> headers, string key, params string[] values)
|
public static void AppendHeaderJoined(IHeaderDictionary headers, string key, params string[] values)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
{
|
{
|
||||||
|
|
@ -135,7 +126,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string existing = GetHeader(headers, key);
|
string existing = GetHeader(headers, key).ToString();
|
||||||
if (existing == null)
|
if (existing == null)
|
||||||
{
|
{
|
||||||
SetHeaderJoined(headers, key, values);
|
SetHeaderJoined(headers, key, values);
|
||||||
|
|
@ -146,7 +137,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AppendHeaderUnmodified(IDictionary<string, StringValues> headers, string key, StringValues values)
|
public static void AppendHeaderUnmodified(IHeaderDictionary headers, string key, StringValues values)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
|
||||||
{
|
|
||||||
internal struct StringSegment : IEquatable<StringSegment>
|
|
||||||
{
|
|
||||||
private readonly string _buffer;
|
|
||||||
private readonly int _offset;
|
|
||||||
private readonly int _count;
|
|
||||||
|
|
||||||
// <summary>
|
|
||||||
// Initializes a new instance of the <see cref="T:System.Object"/> class.
|
|
||||||
// </summary>
|
|
||||||
public StringSegment(string buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
_buffer = buffer;
|
|
||||||
_offset = offset;
|
|
||||||
_count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Buffer
|
|
||||||
{
|
|
||||||
get { return _buffer; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Offset
|
|
||||||
{
|
|
||||||
get { return _offset; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get { return _count; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
get { return _offset == -1 ? null : _buffer.Substring(_offset, _count); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasValue
|
|
||||||
{
|
|
||||||
get { return _offset != -1 && _count != 0 && _buffer != null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(StringSegment other)
|
|
||||||
{
|
|
||||||
return string.Equals(_buffer, other._buffer) && _offset == other._offset && _count == other._count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(null, obj))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj is StringSegment && Equals((StringSegment)obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
unchecked
|
|
||||||
{
|
|
||||||
int hashCode = (_buffer != null ? _buffer.GetHashCode() : 0);
|
|
||||||
hashCode = (hashCode * 397) ^ _offset;
|
|
||||||
hashCode = (hashCode * 397) ^ _count;
|
|
||||||
return hashCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(StringSegment left, StringSegment right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(StringSegment left, StringSegment right)
|
|
||||||
{
|
|
||||||
return !left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool StartsWith(string text, StringComparison comparisonType)
|
|
||||||
{
|
|
||||||
if (text == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
int textLength = text.Length;
|
|
||||||
if (!HasValue || _count < textLength)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Compare(_buffer, _offset, text, 0, textLength, comparisonType) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool EndsWith(string text, StringComparison comparisonType)
|
|
||||||
{
|
|
||||||
if (text == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
int textLength = text.Length;
|
|
||||||
if (!HasValue || _count < textLength)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Compare(_buffer, _offset + _count - textLength, text, 0, textLength, comparisonType) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(string text, StringComparison comparisonType)
|
|
||||||
{
|
|
||||||
if (text == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
int textLength = text.Length;
|
|
||||||
if (!HasValue || _count != textLength)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Compare(_buffer, _offset, text, 0, textLength, comparisonType) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Substring(int offset, int length)
|
|
||||||
{
|
|
||||||
return _buffer.Substring(_offset + offset, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringSegment Subsegment(int offset, int length)
|
|
||||||
{
|
|
||||||
return new StringSegment(_buffer, _offset + offset, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value ?? string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using Microsoft.AspNet.Http.Extensions;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Headers
|
namespace Microsoft.AspNet.Http.Headers
|
||||||
|
|
@ -170,7 +170,7 @@ namespace Microsoft.AspNet.Http.Headers
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return HostString.FromUriComponent(Headers[HeaderNames.Host]);
|
return HostString.FromUriComponent(Headers[HeaderNames.Host].ToString());
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
|
@ -309,17 +309,7 @@ namespace Microsoft.AspNet.Http.Headers
|
||||||
|
|
||||||
public void AppendList<T>(string name, IList<T> values)
|
public void AppendList<T>(string name, IList<T> values)
|
||||||
{
|
{
|
||||||
if (name == null)
|
Headers.AppendList<T>(name, values);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(values));
|
|
||||||
}
|
|
||||||
|
|
||||||
Headers.Append(name, values.Select(value => value.ToString()).ToArray());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNet.Http.Extensions;
|
using Microsoft.AspNet.Http.Extensions;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -135,7 +134,7 @@ namespace Microsoft.AspNet.Http.Headers
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
Uri uri;
|
Uri uri;
|
||||||
if (Uri.TryCreate(Headers[HeaderNames.Location], UriKind.RelativeOrAbsolute, out uri))
|
if (Uri.TryCreate(Headers[HeaderNames.Location].ToString(), UriKind.RelativeOrAbsolute, out uri))
|
||||||
{
|
{
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
@ -206,17 +205,7 @@ namespace Microsoft.AspNet.Http.Headers
|
||||||
|
|
||||||
public void AppendList<T>(string name, IList<T> values)
|
public void AppendList<T>(string name, IList<T> values)
|
||||||
{
|
{
|
||||||
if (name == null)
|
Headers.AppendList<T>(name, values);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(values));
|
|
||||||
}
|
|
||||||
|
|
||||||
Headers.Append(name, values.Select(value => value.ToString()).ToArray());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNet.Http.Extensions
|
||||||
FragmentString fragment = new FragmentString())
|
FragmentString fragment = new FragmentString())
|
||||||
{
|
{
|
||||||
string combinePath = (pathBase.HasValue || path.HasValue) ? (pathBase + path).ToString() : "/";
|
string combinePath = (pathBase.HasValue || path.HasValue) ? (pathBase + path).ToString() : "/";
|
||||||
return combinePath + query + fragment;
|
return $"{combinePath}{query.ToString()}{fragment.ToString()}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Http.Extensions
|
||||||
FragmentString fragment = new FragmentString())
|
FragmentString fragment = new FragmentString())
|
||||||
{
|
{
|
||||||
string combinePath = (pathBase.HasValue || path.HasValue) ? (pathBase + path).ToString() : "/";
|
string combinePath = (pathBase.HasValue || path.HasValue) ? (pathBase + path).ToString() : "/";
|
||||||
return scheme + "://" + host + combinePath + query + fragment;
|
return $"{scheme}://{host.ToString()}{combinePath}{query.ToString()}{fragment.ToString()}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ using Microsoft.Extensions.Primitives;
|
||||||
namespace Microsoft.AspNet.Http
|
namespace Microsoft.AspNet.Http
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents request and response headers
|
/// Represents HttpRequest and HttpResponse headers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IHeaderDictionary : IDictionary<string, StringValues>
|
public interface IHeaderDictionary : IDictionary<string, StringValues>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -140,11 +140,11 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
public override HostString Host
|
public override HostString Host
|
||||||
{
|
{
|
||||||
get { return HostString.FromUriComponent(Headers["Host"]); }
|
get { return HostString.FromUriComponent(Headers["Host"].ToString()); }
|
||||||
set { Headers["Host"] = value.ToUriComponent(); }
|
set { Headers["Host"] = value.ToUriComponent(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IReadableStringCollection Query
|
public override IQueryCollection Query
|
||||||
{
|
{
|
||||||
get { return QueryFeature.Query; }
|
get { return QueryFeature.Query; }
|
||||||
set { QueryFeature.Query = value; }
|
set { QueryFeature.Query = value; }
|
||||||
|
|
@ -161,7 +161,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
get { return HttpRequestFeature.Headers; }
|
get { return HttpRequestFeature.Headers; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IReadableStringCollection Cookies
|
public override IRequestCookieCollection Cookies
|
||||||
{
|
{
|
||||||
get { return RequestCookiesFeature.Cookies; }
|
get { return RequestCookiesFeature.Cookies; }
|
||||||
set { RequestCookiesFeature.Cookies = value; }
|
set { RequestCookiesFeature.Cookies = value; }
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Headers[HeaderNames.ContentType];
|
return Headers[HeaderNames.ContentType].ToString();
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return ParsingHelpers.GetHeaderSplit(HttpRequestFeature.Headers, HeaderNames.WebSocketSubProtocols);
|
return ParsingHelpers.GetHeaderSplit(HttpRequestFeature.Headers, HeaderNames.WebSocketSubProtocols).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,13 @@ using System;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features
|
namespace Microsoft.AspNet.Http.Features
|
||||||
{
|
{
|
||||||
internal sealed class FeatureHelpers
|
internal static class FeatureHelpers
|
||||||
{
|
{
|
||||||
public static T GetAndCache<T>(
|
public static T GetAndCache<T>(
|
||||||
IFeatureCache cache,
|
IFeatureCache cache,
|
||||||
IFeatureCollection features,
|
IFeatureCollection features,
|
||||||
ref T cachedObject)
|
ref T cachedObject)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
cache.CheckFeaturesRevision();
|
cache.CheckFeaturesRevision();
|
||||||
|
|
||||||
|
|
@ -26,6 +27,7 @@ namespace Microsoft.AspNet.Http.Features
|
||||||
public static T GetOrCreate<T>(
|
public static T GetOrCreate<T>(
|
||||||
IFeatureCollection features,
|
IFeatureCollection features,
|
||||||
Func<T> factory)
|
Func<T> factory)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
T obj = features.Get<T>();
|
T obj = features.Get<T>();
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
|
|
@ -43,6 +45,7 @@ namespace Microsoft.AspNet.Http.Features
|
||||||
IFeatureCollection features,
|
IFeatureCollection features,
|
||||||
Func<T> factory,
|
Func<T> factory,
|
||||||
ref T cachedObject)
|
ref T cachedObject)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
cache.CheckFeaturesRevision();
|
cache.CheckFeaturesRevision();
|
||||||
|
|
||||||
|
|
@ -65,6 +68,7 @@ namespace Microsoft.AspNet.Http.Features
|
||||||
IFeatureCollection features,
|
IFeatureCollection features,
|
||||||
Func<IFeatureCollection, T> factory,
|
Func<IFeatureCollection, T> factory,
|
||||||
ref T cachedObject)
|
ref T cachedObject)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
cache.CheckFeaturesRevision();
|
cache.CheckFeaturesRevision();
|
||||||
|
|
||||||
|
|
@ -88,6 +92,7 @@ namespace Microsoft.AspNet.Http.Features
|
||||||
HttpRequest request,
|
HttpRequest request,
|
||||||
Func<HttpRequest, T> factory,
|
Func<HttpRequest, T> factory,
|
||||||
ref T cachedObject)
|
ref T cachedObject)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
cache.CheckFeaturesRevision();
|
cache.CheckFeaturesRevision();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,12 @@
|
||||||
// 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.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.AspNet.WebUtilities;
|
using Microsoft.AspNet.WebUtilities;
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features.Internal
|
namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
|
|
@ -17,6 +15,8 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
public class FormFeature : IFormFeature
|
public class FormFeature : IFormFeature
|
||||||
{
|
{
|
||||||
private readonly HttpRequest _request;
|
private readonly HttpRequest _request;
|
||||||
|
private Task<IFormCollection> _parsedFormTask;
|
||||||
|
private IFormCollection _form;
|
||||||
|
|
||||||
public FormFeature(IFormCollection form)
|
public FormFeature(IFormCollection form)
|
||||||
{
|
{
|
||||||
|
|
@ -63,7 +63,15 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFormCollection Form { get; set; }
|
public IFormCollection Form
|
||||||
|
{
|
||||||
|
get { return _form; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_parsedFormTask = null;
|
||||||
|
_form = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IFormCollection ReadForm()
|
public IFormCollection ReadForm()
|
||||||
{
|
{
|
||||||
|
|
@ -77,17 +85,32 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
throw new InvalidOperationException("Incorrect Content-Type: " + _request.ContentType);
|
throw new InvalidOperationException("Incorrect Content-Type: " + _request.ContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Issue #456 Avoid Sync-over-Async http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx
|
||||||
// TODO: How do we prevent thread exhaustion?
|
// TODO: How do we prevent thread exhaustion?
|
||||||
return ReadFormAsync(CancellationToken.None).GetAwaiter().GetResult();
|
return ReadFormAsync().GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken)
|
public Task<IFormCollection> ReadFormAsync() => ReadFormAsync(CancellationToken.None);
|
||||||
{
|
|
||||||
if (Form != null)
|
|
||||||
{
|
|
||||||
return Form;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// Avoid state machine and task allocation for repeated reads
|
||||||
|
if (_parsedFormTask == null)
|
||||||
|
{
|
||||||
|
if (Form != null)
|
||||||
|
{
|
||||||
|
_parsedFormTask = Task.FromResult(Form);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_parsedFormTask = InnerReadFormAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _parsedFormTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IFormCollection> InnerReadFormAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
if (!HasFormContentType)
|
if (!HasFormContentType)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Incorrect Content-Type: " + _request.ContentType);
|
throw new InvalidOperationException("Incorrect Content-Type: " + _request.ContentType);
|
||||||
|
|
@ -97,18 +120,18 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
|
|
||||||
_request.EnableRewind();
|
_request.EnableRewind();
|
||||||
|
|
||||||
IDictionary<string, StringValues> formFields = null;
|
FormCollection formFields = null;
|
||||||
var files = new FormFileCollection();
|
FormFileCollection files = null;
|
||||||
|
|
||||||
// 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.
|
||||||
using (cancellationToken.Register(_request.HttpContext.Abort))
|
using (cancellationToken.Register((state) => ((HttpContext)state).Abort(), _request.HttpContext))
|
||||||
{
|
{
|
||||||
var contentType = ContentType;
|
var contentType = ContentType;
|
||||||
// Check the content-type
|
// Check the content-type
|
||||||
if (HasApplicationFormContentType(contentType))
|
if (HasApplicationFormContentType(contentType))
|
||||||
{
|
{
|
||||||
var encoding = FilterEncoding(contentType.Encoding);
|
var encoding = FilterEncoding(contentType.Encoding);
|
||||||
formFields = await FormReader.ReadFormAsync(_request.Body, encoding, cancellationToken);
|
formFields = new FormCollection(await FormReader.ReadFormAsync(_request.Body, encoding, cancellationToken));
|
||||||
}
|
}
|
||||||
else if (HasMultipartFormContentType(contentType))
|
else if (HasMultipartFormContentType(contentType))
|
||||||
{
|
{
|
||||||
|
|
@ -119,9 +142,8 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
var section = await multipartReader.ReadNextSectionAsync(cancellationToken);
|
var section = await multipartReader.ReadNextSectionAsync(cancellationToken);
|
||||||
while (section != null)
|
while (section != null)
|
||||||
{
|
{
|
||||||
var headers = new HeaderDictionary(section.Headers);
|
|
||||||
ContentDispositionHeaderValue contentDisposition;
|
ContentDispositionHeaderValue contentDisposition;
|
||||||
ContentDispositionHeaderValue.TryParse(headers[HeaderNames.ContentDisposition], out contentDisposition);
|
ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);
|
||||||
if (HasFileContentDisposition(contentDisposition))
|
if (HasFileContentDisposition(contentDisposition))
|
||||||
{
|
{
|
||||||
// Find the end
|
// Find the end
|
||||||
|
|
@ -129,8 +151,12 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
|
|
||||||
var file = new FormFile(_request.Body, section.BaseStreamOffset.Value, section.Body.Length)
|
var file = new FormFile(_request.Body, section.BaseStreamOffset.Value, section.Body.Length)
|
||||||
{
|
{
|
||||||
Headers = headers,
|
Headers = new HeaderDictionary(section.Headers),
|
||||||
};
|
};
|
||||||
|
if (files == null)
|
||||||
|
{
|
||||||
|
files = new FormFileCollection();
|
||||||
|
}
|
||||||
files.Add(file);
|
files.Add(file);
|
||||||
}
|
}
|
||||||
else if (HasFormDataContentDisposition(contentDisposition))
|
else if (HasFormDataContentDisposition(contentDisposition))
|
||||||
|
|
@ -141,7 +167,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[HeaderNames.ContentType], out mediaType);
|
MediaTypeHeaderValue.TryParse(section.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))
|
||||||
{
|
{
|
||||||
|
|
@ -151,20 +177,35 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + headers[HeaderNames.ContentDisposition]);
|
System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + section.ContentDisposition);
|
||||||
}
|
}
|
||||||
|
|
||||||
section = await multipartReader.ReadNextSectionAsync(cancellationToken);
|
section = await multipartReader.ReadNextSectionAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
formFields = formAccumulator.GetResults();
|
if (formAccumulator.HasValues)
|
||||||
|
{
|
||||||
|
formFields = new FormCollection(formAccumulator.GetResults(), files);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewind so later readers don't have to.
|
// Rewind so later readers don't have to.
|
||||||
_request.Body.Seek(0, SeekOrigin.Begin);
|
_request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
Form = new FormCollection(formFields, files);
|
if (formFields != null)
|
||||||
|
{
|
||||||
|
Form = formFields;
|
||||||
|
}
|
||||||
|
else if (files != null)
|
||||||
|
{
|
||||||
|
Form = new FormCollection(null, files);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Form = FormCollection.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
return Form;
|
return Form;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,13 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
|
|
||||||
public string ContentDisposition
|
public string ContentDisposition
|
||||||
{
|
{
|
||||||
get { return Headers["Content-Disposition"]; }
|
get { return Headers["Content-Disposition"].ToString(); }
|
||||||
set { Headers["Content-Disposition"] = value; }
|
set { Headers["Content-Disposition"] = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ContentType
|
public string ContentType
|
||||||
{
|
{
|
||||||
get { return Headers["Content-Type"]; }
|
get { return Headers["Content-Type"].ToString(); }
|
||||||
set { Headers["Content-Type"] = value; }
|
set { Headers["Content-Type"] = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// 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.Threading;
|
using System.Threading;
|
||||||
using Microsoft.AspNet.Http.Features;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features.Internal
|
namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
public interface IQueryFeature
|
public interface IQueryFeature
|
||||||
{
|
{
|
||||||
IReadableStringCollection Query { get; set; }
|
IQueryCollection Query { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
public interface IRequestCookiesFeature
|
public interface IRequestCookiesFeature
|
||||||
{
|
{
|
||||||
IReadableStringCollection Cookies { get; set; }
|
IRequestCookieCollection Cookies { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,10 +2,8 @@
|
||||||
// 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 Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.AspNet.WebUtilities;
|
using Microsoft.AspNet.WebUtilities;
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Features.Internal
|
namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -17,18 +15,9 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
private IHttpRequestFeature _request;
|
private IHttpRequestFeature _request;
|
||||||
|
|
||||||
private string _original;
|
private string _original;
|
||||||
private IReadableStringCollection _parsedValues;
|
private IQueryCollection _parsedValues;
|
||||||
|
|
||||||
public QueryFeature(IDictionary<string, StringValues> query)
|
public QueryFeature(IQueryCollection query)
|
||||||
: this(new ReadableStringCollection(query))
|
|
||||||
{
|
|
||||||
if (query == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(query));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueryFeature(IReadableStringCollection query)
|
|
||||||
{
|
{
|
||||||
if (query == null)
|
if (query == null)
|
||||||
{
|
{
|
||||||
|
|
@ -62,20 +51,34 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
|
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadableStringCollection Query
|
public IQueryCollection Query
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_features == null)
|
if (_features == null)
|
||||||
{
|
{
|
||||||
return _parsedValues ?? ReadableStringCollection.Empty;
|
if (_parsedValues == null)
|
||||||
|
{
|
||||||
|
_parsedValues = QueryCollection.Empty;
|
||||||
|
}
|
||||||
|
return _parsedValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
var current = HttpRequestFeature.QueryString;
|
var current = HttpRequestFeature.QueryString;
|
||||||
if (_parsedValues == null || !string.Equals(_original, current, StringComparison.Ordinal))
|
if (_parsedValues == null || !string.Equals(_original, current, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
_original = current;
|
_original = current;
|
||||||
_parsedValues = new ReadableStringCollection(QueryHelpers.ParseQuery(current));
|
|
||||||
|
var result = QueryHelpers.ParseNullableQuery(current);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
_parsedValues = QueryCollection.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_parsedValues = new QueryCollection(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return _parsedValues;
|
return _parsedValues;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
@ -18,14 +17,9 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
private IHttpRequestFeature _request;
|
private IHttpRequestFeature _request;
|
||||||
|
|
||||||
private StringValues _original;
|
private StringValues _original;
|
||||||
private IReadableStringCollection _parsedValues;
|
private IRequestCookieCollection _parsedValues;
|
||||||
|
|
||||||
public RequestCookiesFeature(IDictionary<string, StringValues> cookies)
|
public RequestCookiesFeature(IRequestCookieCollection cookies)
|
||||||
: this(new ReadableStringCollection(cookies))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RequestCookiesFeature(IReadableStringCollection cookies)
|
|
||||||
{
|
{
|
||||||
if (cookies == null)
|
if (cookies == null)
|
||||||
{
|
{
|
||||||
|
|
@ -59,32 +53,30 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
|
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadableStringCollection Cookies
|
public IRequestCookieCollection Cookies
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_features == null)
|
if (_features == null)
|
||||||
{
|
{
|
||||||
return _parsedValues ?? ReadableStringCollection.Empty;
|
if (_parsedValues == null)
|
||||||
|
{
|
||||||
|
_parsedValues = RequestCookieCollection.Empty;
|
||||||
|
}
|
||||||
|
return _parsedValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
var headers = HttpRequestFeature.Headers;
|
var headers = HttpRequestFeature.Headers;
|
||||||
StringValues current;
|
StringValues current;
|
||||||
if (!headers.TryGetValue(HeaderNames.Cookie, out current))
|
if (!headers.TryGetValue(HeaderNames.Cookie, out current))
|
||||||
{
|
{
|
||||||
current = StringValues.Empty;
|
current = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_parsedValues == null || !Enumerable.SequenceEqual(_original, current, StringComparer.Ordinal))
|
if (_parsedValues == null || _original != current)
|
||||||
{
|
{
|
||||||
_original = current;
|
_original = current;
|
||||||
var collectionParser = _parsedValues as RequestCookiesCollection;
|
_parsedValues = RequestCookieCollection.Parse(current.ToArray());
|
||||||
if (collectionParser == null)
|
|
||||||
{
|
|
||||||
collectionParser = new RequestCookiesCollection();
|
|
||||||
_parsedValues = collectionParser;
|
|
||||||
}
|
|
||||||
collectionParser.Reparse(current);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _parsedValues;
|
return _parsedValues;
|
||||||
|
|
@ -104,10 +96,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
var headers = new List<string>();
|
var headers = new List<string>();
|
||||||
foreach (var pair in _parsedValues)
|
foreach (var pair in _parsedValues)
|
||||||
{
|
{
|
||||||
foreach (var cookieValue in pair.Value)
|
headers.Add(new CookieHeaderValue(pair.Key, pair.Value).ToString());
|
||||||
{
|
|
||||||
headers.Add(new CookieHeaderValue(pair.Key, cookieValue).ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_original = headers.ToArray();
|
_original = headers.ToArray();
|
||||||
HttpRequestFeature.Headers[HeaderNames.Cookie] = _original;
|
HttpRequestFeature.Headers[HeaderNames.Cookie] = _original;
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
|
||||||
if (_cookiesCollection == null)
|
if (_cookiesCollection == null)
|
||||||
{
|
{
|
||||||
var headers = HttpResponseFeature.Headers;
|
var headers = HttpResponseFeature.Headers;
|
||||||
_cookiesCollection = new ResponseCookies(new HeaderDictionary(headers));
|
_cookiesCollection = new ResponseCookies(headers);
|
||||||
}
|
}
|
||||||
return _cookiesCollection;
|
return _cookiesCollection;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
|
|
@ -10,29 +10,226 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains the parsed form values.
|
/// Contains the parsed form values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FormCollection : ReadableStringCollection, IFormCollection
|
public class FormCollection : IFormCollection
|
||||||
{
|
{
|
||||||
public FormCollection(IDictionary<string, StringValues> store)
|
public static readonly FormCollection Empty = new FormCollection();
|
||||||
: this(store, new FormFileCollection())
|
#if DNXCORE50
|
||||||
|
private static readonly string[] EmptyKeys = Array.Empty<string>();
|
||||||
|
private static readonly StringValues[] EmptyValues = Array.Empty<StringValues>();
|
||||||
|
#else
|
||||||
|
private static readonly string[] EmptyKeys = new string[0];
|
||||||
|
private static readonly StringValues[] EmptyValues = new StringValues[0];
|
||||||
|
#endif
|
||||||
|
private static readonly Enumerator EmptyEnumerator = new Enumerator();
|
||||||
|
// Pre-box
|
||||||
|
private static readonly IEnumerator<KeyValuePair<string, StringValues>> EmptyIEnumeratorType = EmptyEnumerator;
|
||||||
|
private static readonly IEnumerator EmptyIEnumerator = EmptyEnumerator;
|
||||||
|
|
||||||
|
private static IFormFileCollection EmptyFiles = new FormFileCollection();
|
||||||
|
|
||||||
|
private IFormFileCollection _files;
|
||||||
|
|
||||||
|
private FormCollection()
|
||||||
{
|
{
|
||||||
|
// For static Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormCollection(IDictionary<string, StringValues> store, IFormFileCollection files)
|
public FormCollection(Dictionary<string, StringValues> fields, IFormFileCollection files = null)
|
||||||
: base(store)
|
|
||||||
{
|
{
|
||||||
if (store == null)
|
// can be null
|
||||||
{
|
Store = fields;
|
||||||
throw new ArgumentNullException(nameof(store));
|
_files = files;
|
||||||
}
|
|
||||||
|
|
||||||
if (files == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(files));
|
|
||||||
}
|
|
||||||
|
|
||||||
Files = files;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFormFileCollection Files { get; }
|
public IFormFileCollection Files
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _files ?? EmptyFiles;
|
||||||
|
}
|
||||||
|
private set { _files = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, StringValues> Store { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or sets 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 StringValues or StringValues.Empty if the key is not present.</returns>
|
||||||
|
public StringValues this[string key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return StringValues.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringValues value;
|
||||||
|
if (TryGetValue(key, out value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return StringValues.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of elements contained in the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" />;.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The number of elements contained in the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" />.</returns>
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Store.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<string> Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return EmptyKeys;
|
||||||
|
}
|
||||||
|
return Store.Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> contains a specific key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key.</param>
|
||||||
|
/// <returns>true if the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> contains a specific key; otherwise, false.</returns>
|
||||||
|
public bool ContainsKey(string key)
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Store.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a value from the dictionary.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The header name.</param>
|
||||||
|
/// <param name="value">The value.</param>
|
||||||
|
/// <returns>true if the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> contains the key; otherwise, false.</returns>
|
||||||
|
public bool TryGetValue(string key, out StringValues value)
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
value = default(StringValues);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Store.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an struct enumerator that iterates through a collection without boxing and is also used via the <see cref="T:Microsoft.AspNet.Http.IFormCollection" /> interface.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:Microsoft.AspNet.Http.StructEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
public Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyEnumerator;
|
||||||
|
}
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return new Enumerator(Store.GetEnumerator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection, boxes in non-empty path.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
IEnumerator<KeyValuePair<string, StringValues>> IEnumerable<KeyValuePair<string, StringValues>>.GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyIEnumeratorType;
|
||||||
|
}
|
||||||
|
// Boxed Enumerator
|
||||||
|
return Store.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection, boxes in non-empty path.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyIEnumerator;
|
||||||
|
}
|
||||||
|
// Boxed Enumerator
|
||||||
|
return Store.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
|
||||||
|
{
|
||||||
|
// Do NOT make this readonly, or MoveNext will not work
|
||||||
|
private Dictionary<string, StringValues>.Enumerator _dictionaryEnumerator;
|
||||||
|
private bool _notEmpty;
|
||||||
|
|
||||||
|
internal Enumerator(Dictionary<string, StringValues>.Enumerator dictionaryEnumerator)
|
||||||
|
{
|
||||||
|
_dictionaryEnumerator = dictionaryEnumerator;
|
||||||
|
_notEmpty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
return _dictionaryEnumerator.MoveNext();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyValuePair<string, StringValues> Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
return _dictionaryEnumerator.Current;
|
||||||
|
}
|
||||||
|
return default(KeyValuePair<string, StringValues>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IEnumerator.Reset()
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
((IEnumerator)_dictionaryEnumerator).Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,64 +9,37 @@ using Microsoft.Extensions.Primitives;
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a wrapper for owin.RequestHeaders and owin.ResponseHeaders.
|
/// Represents a wrapper for RequestHeaders and ResponseHeaders.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class HeaderDictionary : IHeaderDictionary
|
public class HeaderDictionary : IHeaderDictionary
|
||||||
{
|
{
|
||||||
public HeaderDictionary() : this(new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase))
|
#if DNXCORE50
|
||||||
|
private static readonly string[] EmptyKeys = Array.Empty<string>();
|
||||||
|
private static readonly StringValues[] EmptyValues = Array.Empty<StringValues>();
|
||||||
|
#else
|
||||||
|
private static readonly string[] EmptyKeys = new string[0];
|
||||||
|
private static readonly StringValues[] EmptyValues = new StringValues[0];
|
||||||
|
#endif
|
||||||
|
private static readonly Enumerator EmptyEnumerator = new Enumerator();
|
||||||
|
// Pre-box
|
||||||
|
private static readonly IEnumerator<KeyValuePair<string, StringValues>> EmptyIEnumeratorType = EmptyEnumerator;
|
||||||
|
private static readonly IEnumerator EmptyIEnumerator = EmptyEnumerator;
|
||||||
|
|
||||||
|
public HeaderDictionary()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public HeaderDictionary(Dictionary<string, StringValues> store)
|
||||||
/// Initializes a new instance of the <see cref="T:Microsoft.Owin.HeaderDictionary" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="store">The underlying data store.</param>
|
|
||||||
public HeaderDictionary(IDictionary<string, StringValues> store)
|
|
||||||
{
|
{
|
||||||
if (store == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(store));
|
|
||||||
}
|
|
||||||
|
|
||||||
Store = store;
|
Store = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDictionary<string, StringValues> Store { get; set; }
|
public HeaderDictionary(int capacity)
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets an <see cref="T:System.Collections.ICollection" /> that contains the keys in the <see cref="T:Microsoft.Owin.HeaderDictionary" />;.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>An <see cref="T:System.Collections.ICollection" /> that contains the keys in the <see cref="T:Microsoft.Owin.HeaderDictionary" />.</returns>
|
|
||||||
public ICollection<string> Keys
|
|
||||||
{
|
{
|
||||||
get { return Store.Keys; }
|
Store = new Dictionary<string, StringValues>(capacity, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private Dictionary<string, StringValues> Store { get; set; }
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public ICollection<StringValues> Values
|
|
||||||
{
|
|
||||||
get { return Store.Values; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of elements contained in the <see cref="T:Microsoft.Owin.HeaderDictionary" />;.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The number of elements contained in the <see cref="T:Microsoft.Owin.HeaderDictionary" />.</returns>
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get { return Store.Count; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value that indicates whether the <see cref="T:Microsoft.Owin.HeaderDictionary" /> is in read-only mode.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>true if the <see cref="T:Microsoft.Owin.HeaderDictionary" /> is in read-only mode; otherwise, false.</returns>
|
|
||||||
public bool IsReadOnly
|
|
||||||
{
|
|
||||||
get { return Store.IsReadOnly; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
|
|
@ -75,8 +48,47 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <returns>the associated value from the collection as a StringValues or StringValues.Empty 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 StringValues this[string key]
|
public StringValues this[string key]
|
||||||
{
|
{
|
||||||
get { return ParsingHelpers.GetHeader(Store, key); }
|
get
|
||||||
set { ParsingHelpers.SetHeader(Store, key, value); }
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return StringValues.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringValues value;
|
||||||
|
if (TryGetValue(key, out value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return StringValues.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringValues.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Store.Remove(key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
Store = new Dictionary<string, StringValues>(1, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
Store[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -87,25 +99,76 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
StringValues IDictionary<string, StringValues>.this[string key]
|
StringValues IDictionary<string, StringValues>.this[string key]
|
||||||
{
|
{
|
||||||
get { return Store[key]; }
|
get { return Store[key]; }
|
||||||
set { Store[key] = value; }
|
set { this[key] = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns an enumerator that iterates through a collection.
|
/// Gets the number of elements contained in the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" />;.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
/// <returns>The number of elements contained in the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" />.</returns>
|
||||||
IEnumerator<KeyValuePair<string, StringValues>> IEnumerable<KeyValuePair<string, StringValues>>.GetEnumerator()
|
public int Count
|
||||||
{
|
{
|
||||||
return Store.GetEnumerator();
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Store.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns an enumerator that iterates through a collection.
|
/// Gets a value that indicates whether the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> is in read-only mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
/// <returns>true if the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> is in read-only mode; otherwise, false.</returns>
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
public bool IsReadOnly
|
||||||
{
|
{
|
||||||
return Store.GetEnumerator();
|
get
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<string> Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return EmptyKeys;
|
||||||
|
}
|
||||||
|
return Store.Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<StringValues> Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return EmptyValues;
|
||||||
|
}
|
||||||
|
return Store.Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new list of items to the collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to add.</param>
|
||||||
|
public void Add(KeyValuePair<string, StringValues> item)
|
||||||
|
{
|
||||||
|
if (item.Key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("The key is null");
|
||||||
|
}
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
Store = new Dictionary<string, StringValues>(1, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
Store.Add(item.Key, item.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -115,54 +178,27 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <param name="value">The header values.</param>
|
/// <param name="value">The header values.</param>
|
||||||
public void Add(string key, StringValues value)
|
public void Add(string key, StringValues value)
|
||||||
{
|
{
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
Store = new Dictionary<string, StringValues>(1);
|
||||||
|
}
|
||||||
Store.Add(key, value);
|
Store.Add(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the <see cref="T:Microsoft.Owin.HeaderDictionary" /> contains a specific key.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key.</param>
|
|
||||||
/// <returns>true if the <see cref="T:Microsoft.Owin.HeaderDictionary" /> contains a specific key; otherwise, false.</returns>
|
|
||||||
public bool ContainsKey(string key)
|
|
||||||
{
|
|
||||||
return Store.ContainsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the given header from the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
|
|
||||||
public bool Remove(string key)
|
|
||||||
{
|
|
||||||
return Store.Remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves a value from the dictionary.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The header name.</param>
|
|
||||||
/// <param name="value">The value.</param>
|
|
||||||
/// <returns>true if the <see cref="T:Microsoft.Owin.HeaderDictionary" /> contains the key; otherwise, false.</returns>
|
|
||||||
public bool TryGetValue(string key, out StringValues value)
|
|
||||||
{
|
|
||||||
return Store.TryGetValue(key, out value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a new list of items to the collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">The item to add.</param>
|
|
||||||
public void Add(KeyValuePair<string, StringValues> item)
|
|
||||||
{
|
|
||||||
Store.Add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clears the entire list of objects.
|
/// Clears the entire list of objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
Store.Clear();
|
Store.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,17 +209,47 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <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, StringValues> item)
|
public bool Contains(KeyValuePair<string, StringValues> item)
|
||||||
{
|
{
|
||||||
return Store.Contains(item);
|
StringValues value;
|
||||||
|
if (Store == null ||
|
||||||
|
!Store.TryGetValue(item.Key, out value) ||
|
||||||
|
!StringValues.Equals(value, item.Value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies the <see cref="T:Microsoft.Owin.HeaderDictionary" /> elements to a one-dimensional Array instance at the specified index.
|
/// Determines whether the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> contains a specific key.
|
||||||
/// </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="key">The key.</param>
|
||||||
|
/// <returns>true if the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> contains a specific key; otherwise, false.</returns>
|
||||||
|
public bool ContainsKey(string key)
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Store.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> elements to a one-dimensional Array instance at the specified index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">The one-dimensional Array that is the destination of the specified objects copied from the <see cref="T:Microsoft.AspNet.Http.Internal.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, StringValues>[] array, int arrayIndex)
|
public void CopyTo(KeyValuePair<string, StringValues>[] array, int arrayIndex)
|
||||||
{
|
{
|
||||||
Store.CopyTo(array, arrayIndex);
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in Store)
|
||||||
|
{
|
||||||
|
array[arrayIndex] = item;
|
||||||
|
arrayIndex++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -193,7 +259,144 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <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, StringValues> item)
|
public bool Remove(KeyValuePair<string, StringValues> item)
|
||||||
{
|
{
|
||||||
return Store.Remove(item);
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringValues value;
|
||||||
|
|
||||||
|
if (Store.TryGetValue(item.Key, out value) && StringValues.Equals(item.Value, value))
|
||||||
|
{
|
||||||
|
return Store.Remove(item.Key);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the given header from the collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The header name.</param>
|
||||||
|
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
|
||||||
|
public bool Remove(string key)
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Store.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a value from the dictionary.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The header name.</param>
|
||||||
|
/// <param name="value">The value.</param>
|
||||||
|
/// <returns>true if the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> contains the key; otherwise, false.</returns>
|
||||||
|
public bool TryGetValue(string key, out StringValues value)
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
value = default(StringValues);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Store.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
public Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyEnumerator;
|
||||||
|
}
|
||||||
|
return new Enumerator(Store.GetEnumerator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
IEnumerator<KeyValuePair<string, StringValues>> IEnumerable<KeyValuePair<string, StringValues>>.GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyIEnumeratorType;
|
||||||
|
}
|
||||||
|
return Store.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyIEnumerator;
|
||||||
|
}
|
||||||
|
return Store.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
|
||||||
|
{
|
||||||
|
// Do NOT make this readonly, or MoveNext will not work
|
||||||
|
private Dictionary<string, StringValues>.Enumerator _dictionaryEnumerator;
|
||||||
|
private bool _notEmpty;
|
||||||
|
|
||||||
|
internal Enumerator(Dictionary<string, StringValues>.Enumerator dictionaryEnumerator)
|
||||||
|
{
|
||||||
|
_dictionaryEnumerator = dictionaryEnumerator;
|
||||||
|
_notEmpty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
return _dictionaryEnumerator.MoveNext();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyValuePair<string, StringValues> Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
return _dictionaryEnumerator.Current;
|
||||||
|
}
|
||||||
|
return default(KeyValuePair<string, StringValues>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IEnumerator.Reset()
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
((IEnumerator)_dictionaryEnumerator).Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,12 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
bool ICollection<KeyValuePair<object, object>>.Remove(KeyValuePair<object, object> item)
|
bool ICollection<KeyValuePair<object, object>>.Remove(KeyValuePair<object, object> item)
|
||||||
{
|
{
|
||||||
return Items.Remove(item);
|
object value;
|
||||||
|
if (Items.TryGetValue(item.Key, out value) && Equals(item.Value, value))
|
||||||
|
{
|
||||||
|
return Items.Remove(item.Key);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator<KeyValuePair<object, object>> IEnumerable<KeyValuePair<object, object>>.GetEnumerator()
|
IEnumerator<KeyValuePair<object, object>> IEnumerable<KeyValuePair<object, object>>.GetEnumerator()
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
public bool Equals(HeaderSegmentCollection other)
|
public bool Equals(HeaderSegmentCollection other)
|
||||||
{
|
{
|
||||||
return Equals(_headers, other._headers);
|
return StringValues.Equals(_headers, other._headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
|
|
@ -363,13 +363,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
|
|
||||||
internal static class ParsingHelpers
|
internal static class ParsingHelpers
|
||||||
{
|
{
|
||||||
public static StringValues GetHeader(IDictionary<string, StringValues> headers, string key)
|
public static StringValues GetHeaderSplit(IHeaderDictionary headers, string key)
|
||||||
{
|
|
||||||
StringValues value;
|
|
||||||
return headers.TryGetValue(key, out value) ? value : StringValues.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static StringValues GetHeaderSplit(IDictionary<string, StringValues> headers, string key)
|
|
||||||
{
|
{
|
||||||
var values = GetHeaderUnmodified(headers, key);
|
var values = GetHeaderUnmodified(headers, key);
|
||||||
return new StringValues(GetHeaderSplitImplementation(values).ToArray());
|
return new StringValues(GetHeaderSplitImplementation(values).ToArray());
|
||||||
|
|
@ -386,7 +380,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StringValues GetHeaderUnmodified(IDictionary<string, StringValues> headers, string key)
|
public static StringValues GetHeaderUnmodified(IHeaderDictionary headers, string key)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
{
|
{
|
||||||
|
|
@ -397,32 +391,6 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
return headers.TryGetValue(key, out values) ? values : StringValues.Empty;
|
return headers.TryGetValue(key, out values) ? values : StringValues.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetHeader(IDictionary<string, StringValues> headers, string key, StringValues value)
|
|
||||||
{
|
|
||||||
if (headers == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(headers));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(key))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(key));
|
|
||||||
}
|
|
||||||
if (StringValues.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
headers.Remove(key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
headers[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string DeQuote(string value)
|
private static string DeQuote(string value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The HttpRequest query string collection
|
||||||
|
/// </summary>
|
||||||
|
public class QueryCollection : IQueryCollection
|
||||||
|
{
|
||||||
|
public static readonly QueryCollection Empty = new QueryCollection();
|
||||||
|
#if DNXCORE50
|
||||||
|
private static readonly string[] EmptyKeys = Array.Empty<string>();
|
||||||
|
private static readonly StringValues[] EmptyValues = Array.Empty<StringValues>();
|
||||||
|
#else
|
||||||
|
private static readonly string[] EmptyKeys = new string[0];
|
||||||
|
private static readonly StringValues[] EmptyValues = new StringValues[0];
|
||||||
|
#endif
|
||||||
|
private static readonly Enumerator EmptyEnumerator = new Enumerator();
|
||||||
|
// Pre-box
|
||||||
|
private static readonly IEnumerator<KeyValuePair<string, StringValues>> EmptyIEnumeratorType = EmptyEnumerator;
|
||||||
|
private static readonly IEnumerator EmptyIEnumerator = EmptyEnumerator;
|
||||||
|
|
||||||
|
private Dictionary<string, StringValues> Store { get; set; }
|
||||||
|
|
||||||
|
public QueryCollection()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryCollection(Dictionary<string, StringValues> store)
|
||||||
|
{
|
||||||
|
Store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryCollection(QueryCollection store)
|
||||||
|
{
|
||||||
|
Store = store.Store;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryCollection(int capacity)
|
||||||
|
{
|
||||||
|
Store = new Dictionary<string, StringValues>(capacity, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or sets 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 StringValues or StringValues.Empty if the key is not present.</returns>
|
||||||
|
public StringValues this[string key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return StringValues.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringValues value;
|
||||||
|
if (TryGetValue(key, out value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return StringValues.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of elements contained in the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" />;.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The number of elements contained in the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" />.</returns>
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Store.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<string> Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return EmptyKeys;
|
||||||
|
}
|
||||||
|
return Store.Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> contains a specific key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key.</param>
|
||||||
|
/// <returns>true if the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> contains a specific key; otherwise, false.</returns>
|
||||||
|
public bool ContainsKey(string key)
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Store.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a value from the dictionary.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The header name.</param>
|
||||||
|
/// <param name="value">The value.</param>
|
||||||
|
/// <returns>true if the <see cref="T:Microsoft.AspNet.Http.Internal.HeaderDictionary" /> contains the key; otherwise, false.</returns>
|
||||||
|
public bool TryGetValue(string key, out StringValues value)
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
value = default(StringValues);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Store.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
public Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyEnumerator;
|
||||||
|
}
|
||||||
|
return new Enumerator(Store.GetEnumerator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
IEnumerator<KeyValuePair<string, StringValues>> IEnumerable<KeyValuePair<string, StringValues>>.GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyIEnumeratorType;
|
||||||
|
}
|
||||||
|
return Store.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyIEnumerator;
|
||||||
|
}
|
||||||
|
return Store.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
|
||||||
|
{
|
||||||
|
// Do NOT make this readonly, or MoveNext will not work
|
||||||
|
private Dictionary<string, StringValues>.Enumerator _dictionaryEnumerator;
|
||||||
|
private bool _notEmpty;
|
||||||
|
|
||||||
|
internal Enumerator(Dictionary<string, StringValues>.Enumerator dictionaryEnumerator)
|
||||||
|
{
|
||||||
|
_dictionaryEnumerator = dictionaryEnumerator;
|
||||||
|
_notEmpty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
return _dictionaryEnumerator.MoveNext();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyValuePair<string, StringValues> Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
return _dictionaryEnumerator.Current;
|
||||||
|
}
|
||||||
|
return default(KeyValuePair<string, StringValues>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IEnumerator.Reset()
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
((IEnumerator)_dictionaryEnumerator).Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Accessors for query, forms, etc.
|
|
||||||
/// </summary>
|
|
||||||
public class ReadableStringCollection : IReadableStringCollection
|
|
||||||
{
|
|
||||||
public static readonly IReadableStringCollection Empty = new ReadableStringCollection(new Dictionary<string, StringValues>(0));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new wrapper
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="store"></param>
|
|
||||||
public ReadableStringCollection(IDictionary<string, StringValues> store)
|
|
||||||
{
|
|
||||||
if (store == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(store));
|
|
||||||
}
|
|
||||||
|
|
||||||
Store = store;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IDictionary<string, StringValues> Store { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of elements contained in the collection.
|
|
||||||
/// </summary>
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get { return Store.Count; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a collection containing the keys.
|
|
||||||
/// </summary>
|
|
||||||
public ICollection<string> Keys
|
|
||||||
{
|
|
||||||
get { return Store.Keys; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the associated value from the collection. Multiple values will be merged.
|
|
||||||
/// Returns StringValues.Empty if the key is not present.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public StringValues this[string key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
StringValues value;
|
|
||||||
if (Store.TryGetValue(key, out value))
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return StringValues.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the collection contains an element with the specified key.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool ContainsKey(string key)
|
|
||||||
{
|
|
||||||
return Store.ContainsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public IEnumerator<KeyValuePair<string, StringValues>> GetEnumerator()
|
|
||||||
{
|
|
||||||
return Store.GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
if (value < 0 || value > Length)
|
if (value < 0 || value > Length)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(value), value, "The Position must be within the length of the Stream: " + Length);
|
throw new ArgumentOutOfRangeException(nameof(value), value, "The Position must be within the length of the Stream: " + Length.ToString());
|
||||||
}
|
}
|
||||||
VerifyPosition();
|
VerifyPosition();
|
||||||
_position = value;
|
_position = value;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Http.Internal
|
||||||
|
{
|
||||||
|
public class RequestCookieCollection : IRequestCookieCollection
|
||||||
|
{
|
||||||
|
public static readonly RequestCookieCollection Empty = new RequestCookieCollection();
|
||||||
|
#if DNXCORE50
|
||||||
|
private static readonly string[] EmptyKeys = Array.Empty<string>();
|
||||||
|
#else
|
||||||
|
private static readonly string[] EmptyKeys = new string[0];
|
||||||
|
#endif
|
||||||
|
private static readonly Enumerator EmptyEnumerator = new Enumerator();
|
||||||
|
// Pre-box
|
||||||
|
private static readonly IEnumerator<KeyValuePair<string, string>> EmptyIEnumeratorType = EmptyEnumerator;
|
||||||
|
private static readonly IEnumerator EmptyIEnumerator = EmptyEnumerator;
|
||||||
|
|
||||||
|
private Dictionary<string, string> Store { get; set; }
|
||||||
|
|
||||||
|
public RequestCookieCollection()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestCookieCollection(Dictionary<string, string> store)
|
||||||
|
{
|
||||||
|
Store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestCookieCollection(int capacity)
|
||||||
|
{
|
||||||
|
Store = new Dictionary<string, string>(capacity, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string this[string key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
string value;
|
||||||
|
if (TryGetValue(key, out value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RequestCookieCollection Parse(IList<string> values)
|
||||||
|
{
|
||||||
|
if (values.Count == 0)
|
||||||
|
{
|
||||||
|
return Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
IList<CookieHeaderValue> cookies;
|
||||||
|
if (CookieHeaderValue.TryParseList(values, out cookies))
|
||||||
|
{
|
||||||
|
if (cookies.Count == 0)
|
||||||
|
{
|
||||||
|
return Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var store = new Dictionary<string, string>(cookies.Count);
|
||||||
|
for (var i = 0; i < cookies.Count; i++)
|
||||||
|
{
|
||||||
|
var cookie = cookies[i];
|
||||||
|
var name = Uri.UnescapeDataString(cookie.Name.Replace('+', ' '));
|
||||||
|
var value = Uri.UnescapeDataString(cookie.Value.Replace('+', ' '));
|
||||||
|
store[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RequestCookieCollection(store);
|
||||||
|
}
|
||||||
|
return Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Store.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<string> Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return EmptyKeys;
|
||||||
|
}
|
||||||
|
return Store.Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(string key)
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Store.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(string key, out string value)
|
||||||
|
{
|
||||||
|
if (Store == null)
|
||||||
|
{
|
||||||
|
value = string.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Store.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an struct enumerator that iterates through a collection without boxing.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:Microsoft.AspNet.Http.Internal.RequestCookies.Enumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
public Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyEnumerator;
|
||||||
|
}
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return new Enumerator(Store.GetEnumerator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection, boxes in non-empty path.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyIEnumeratorType;
|
||||||
|
}
|
||||||
|
// Boxed Enumerator
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through a collection, boxes in non-empty path.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
if (Store == null || Store.Count == 0)
|
||||||
|
{
|
||||||
|
// Non-boxed Enumerator
|
||||||
|
return EmptyIEnumerator;
|
||||||
|
}
|
||||||
|
// Boxed Enumerator
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Enumerator : IEnumerator<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
// Do NOT make this readonly, or MoveNext will not work
|
||||||
|
private Dictionary<string, string>.Enumerator _dictionaryEnumerator;
|
||||||
|
private bool _notEmpty;
|
||||||
|
|
||||||
|
internal Enumerator(Dictionary<string, string>.Enumerator dictionaryEnumerator)
|
||||||
|
{
|
||||||
|
_dictionaryEnumerator = dictionaryEnumerator;
|
||||||
|
_notEmpty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
return _dictionaryEnumerator.MoveNext();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyValuePair<string, string> Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
var current = _dictionaryEnumerator.Current;
|
||||||
|
return new KeyValuePair<string, string>(current.Key, current.Value);
|
||||||
|
}
|
||||||
|
return default(KeyValuePair<string, string>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
if (_notEmpty)
|
||||||
|
{
|
||||||
|
((IEnumerator)_dictionaryEnumerator).Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Http.Internal
|
|
||||||
{
|
|
||||||
public class RequestCookiesCollection : IReadableStringCollection
|
|
||||||
{
|
|
||||||
private readonly IDictionary<string, string> _dictionary;
|
|
||||||
|
|
||||||
public RequestCookiesCollection()
|
|
||||||
{
|
|
||||||
_dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringValues this[string key]
|
|
||||||
{
|
|
||||||
get { return Get(key); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of elements contained in the collection.
|
|
||||||
/// </summary>
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get { return _dictionary.Count; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a collection containing the keys.
|
|
||||||
/// </summary>
|
|
||||||
public ICollection<string> Keys
|
|
||||||
{
|
|
||||||
get { return _dictionary.Keys; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the collection contains an element with the specified key.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool ContainsKey(string key)
|
|
||||||
{
|
|
||||||
return _dictionary.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)
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
return _dictionary.TryGetValue(key, out value) ? value : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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 value;
|
|
||||||
return _dictionary.TryGetValue(key, out value) ? new[] { value } : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reparse(IList<string> values)
|
|
||||||
{
|
|
||||||
_dictionary.Clear();
|
|
||||||
|
|
||||||
IList<CookieHeaderValue> cookies;
|
|
||||||
if (CookieHeaderValue.TryParseList(values, out cookies))
|
|
||||||
{
|
|
||||||
foreach (var cookie in cookies)
|
|
||||||
{
|
|
||||||
var name = Uri.UnescapeDataString(cookie.Name.Replace('+', ' '));
|
|
||||||
var value = Uri.UnescapeDataString(cookie.Value.Replace('+', ' '));
|
|
||||||
_dictionary[name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<KeyValuePair<string, StringValues>> GetEnumerator()
|
|
||||||
{
|
|
||||||
foreach (var pair in _dictionary)
|
|
||||||
{
|
|
||||||
yield return new KeyValuePair<string, StringValues>(pair.Key, pair.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
// 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.Linq;
|
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -80,10 +80,10 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
public void Delete(string key)
|
public void Delete(string key)
|
||||||
{
|
{
|
||||||
var encodedKeyPlusEquals = UrlEncoder.Default.Encode(key) + "=";
|
var encodedKeyPlusEquals = $"{UrlEncoder.Default.Encode(key)}=";
|
||||||
Func<string, bool> predicate = value => value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
|
Func<string, string, bool> predicate = (value, encKeyPlusEquals) => value.StartsWith(encKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
StringValues deleteCookies = encodedKeyPlusEquals + "; expires=Thu, 01-Jan-1970 00:00:00 GMT";
|
StringValues deleteCookies = $"{encodedKeyPlusEquals}; expires=Thu, 01-Jan-1970 00:00:00 GMT";
|
||||||
var existingValues = Headers[HeaderNames.SetCookie];
|
var existingValues = Headers[HeaderNames.SetCookie];
|
||||||
if (StringValues.IsNullOrEmpty(existingValues))
|
if (StringValues.IsNullOrEmpty(existingValues))
|
||||||
{
|
{
|
||||||
|
|
@ -91,7 +91,24 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Headers[HeaderNames.SetCookie] = existingValues.Where(value => !predicate(value)).Concat(deleteCookies).ToArray();
|
var values = existingValues.ToArray();
|
||||||
|
var newValues = new List<string>();
|
||||||
|
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
if (!predicate(values[i], encodedKeyPlusEquals))
|
||||||
|
{
|
||||||
|
newValues.Add(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
values = deleteCookies.ToArray();
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
newValues.Add(values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Headers[HeaderNames.SetCookie] = new StringValues(newValues.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,33 +123,44 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(options));
|
throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
var encodedKeyPlusEquals = UrlEncoder.Default.Encode(key) + "=";
|
var encodedKeyPlusEquals = $"{UrlEncoder.Default.Encode(key)}=";
|
||||||
bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
|
bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
|
||||||
bool pathHasValue = !string.IsNullOrEmpty(options.Path);
|
bool pathHasValue = !string.IsNullOrEmpty(options.Path);
|
||||||
|
|
||||||
Func<string, bool> rejectPredicate;
|
Func<string, string, CookieOptions, bool> rejectPredicate;
|
||||||
if (domainHasValue)
|
if (domainHasValue)
|
||||||
{
|
{
|
||||||
rejectPredicate = value =>
|
rejectPredicate = (value, encKeyPlusEquals, opts) =>
|
||||||
value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase) &&
|
value.StartsWith(encKeyPlusEquals, StringComparison.OrdinalIgnoreCase) &&
|
||||||
value.IndexOf("domain=" + options.Domain, StringComparison.OrdinalIgnoreCase) != -1;
|
value.IndexOf($"domain={opts.Domain}", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
}
|
}
|
||||||
else if (pathHasValue)
|
else if (pathHasValue)
|
||||||
{
|
{
|
||||||
rejectPredicate = value =>
|
rejectPredicate = (value, encKeyPlusEquals, opts) =>
|
||||||
value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase) &&
|
value.StartsWith(encKeyPlusEquals, StringComparison.OrdinalIgnoreCase) &&
|
||||||
value.IndexOf("path=" + options.Path, StringComparison.OrdinalIgnoreCase) != -1;
|
value.IndexOf($"path={opts.Path}", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rejectPredicate = value => value.StartsWith(encodedKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
|
rejectPredicate = (value, encKeyPlusEquals, opts) => value.StartsWith(encKeyPlusEquals, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingValues = Headers[HeaderNames.SetCookie];
|
var existingValues = Headers[HeaderNames.SetCookie];
|
||||||
if (!StringValues.IsNullOrEmpty(existingValues))
|
if (!StringValues.IsNullOrEmpty(existingValues))
|
||||||
{
|
{
|
||||||
Headers[HeaderNames.SetCookie] = existingValues.Where(value => !rejectPredicate(value)).ToArray();
|
var values = existingValues.ToArray();
|
||||||
|
var newValues = new List<string>();
|
||||||
|
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
if (!rejectPredicate(values[i], encodedKeyPlusEquals, options))
|
||||||
|
{
|
||||||
|
newValues.Add(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Headers[HeaderNames.SetCookie] = new StringValues(newValues.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
Append(key, string.Empty, new CookieOptions
|
Append(key, string.Empty, new CookieOptions
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,7 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
if (minCount > _buffer.Length)
|
if (minCount > _buffer.Length)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(minCount), minCount, "The value must be smaller than the buffer size: " + _buffer.Length);
|
throw new ArgumentOutOfRangeException(nameof(minCount), minCount, "The value must be smaller than the buffer size: " + _buffer.Length.ToString());
|
||||||
}
|
}
|
||||||
while (_bufferCount < minCount)
|
while (_bufferCount < minCount)
|
||||||
{
|
{
|
||||||
|
|
@ -289,7 +289,7 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
if (minCount > _buffer.Length)
|
if (minCount > _buffer.Length)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(minCount), minCount, "The value must be smaller than the buffer size: " + _buffer.Length);
|
throw new ArgumentOutOfRangeException(nameof(minCount), minCount, "The value must be smaller than the buffer size: " + _buffer.Length.ToString());
|
||||||
}
|
}
|
||||||
while (_bufferCount < minCount)
|
while (_bufferCount < minCount)
|
||||||
{
|
{
|
||||||
|
|
@ -315,38 +315,42 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
public string ReadLine(int lengthLimit)
|
public string ReadLine(int lengthLimit)
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
var builder = new MemoryStream(200);
|
using (var builder = new MemoryStream(200))
|
||||||
bool foundCR = false, foundCRLF = false;
|
|
||||||
|
|
||||||
while (!foundCRLF && EnsureBuffered())
|
|
||||||
{
|
{
|
||||||
if (builder.Length > lengthLimit)
|
bool foundCR = false, foundCRLF = false;
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Line length limit exceeded: " + lengthLimit);
|
|
||||||
}
|
|
||||||
ProcessLineChar(builder, ref foundCR, ref foundCRLF);
|
|
||||||
}
|
|
||||||
|
|
||||||
return DecodeLine(builder, foundCRLF);
|
while (!foundCRLF && EnsureBuffered())
|
||||||
|
{
|
||||||
|
if (builder.Length > lengthLimit)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Line length limit exceeded: " + lengthLimit.ToString());
|
||||||
|
}
|
||||||
|
ProcessLineChar(builder, ref foundCR, ref foundCRLF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecodeLine(builder, foundCRLF);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> ReadLineAsync(int lengthLimit, CancellationToken cancellationToken)
|
public async Task<string> ReadLineAsync(int lengthLimit, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
var builder = new MemoryStream(200);
|
using (var builder = new MemoryStream(200))
|
||||||
bool foundCR = false, foundCRLF = false;
|
|
||||||
|
|
||||||
while (!foundCRLF && await EnsureBufferedAsync(cancellationToken))
|
|
||||||
{
|
{
|
||||||
if (builder.Length > lengthLimit)
|
bool foundCR = false, foundCRLF = false;
|
||||||
|
|
||||||
|
while (!foundCRLF && await EnsureBufferedAsync(cancellationToken))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Line length limit exceeded: " + lengthLimit);
|
if (builder.Length > lengthLimit)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Line length limit exceeded: " + lengthLimit.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessLineChar(builder, ref foundCR, ref foundCRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessLineChar(builder, ref foundCR, ref foundCRLF);
|
return DecodeLine(builder, foundCRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DecodeLine(builder, foundCRLF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessLineChar(MemoryStream builder, ref bool foundCR, ref bool foundCRLF)
|
private void ProcessLineChar(MemoryStream builder, ref bool foundCR, ref bool foundCRLF)
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,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, StringValues>> ReadFormAsync(Stream stream, CancellationToken cancellationToken = new CancellationToken())
|
public static Task<Dictionary<string, StringValues>> ReadFormAsync(Stream stream, CancellationToken cancellationToken = new CancellationToken())
|
||||||
{
|
{
|
||||||
return ReadFormAsync(stream, Encoding.UTF8, cancellationToken);
|
return ReadFormAsync(stream, Encoding.UTF8, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +195,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 async Task<IDictionary<string, StringValues>> ReadFormAsync(Stream stream, Encoding encoding, CancellationToken cancellationToken = new CancellationToken())
|
public static async Task<Dictionary<string, StringValues>> ReadFormAsync(Stream stream, Encoding encoding, CancellationToken cancellationToken = new CancellationToken())
|
||||||
{
|
{
|
||||||
var reader = new FormReader(stream, encoding);
|
var reader = new FormReader(stream, encoding);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,16 @@ using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebUtilities
|
namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
public class KeyValueAccumulator
|
public struct KeyValueAccumulator
|
||||||
{
|
{
|
||||||
private Dictionary<string, List<string>> _accumulator;
|
private Dictionary<string, List<string>> _accumulator;
|
||||||
|
|
||||||
public KeyValueAccumulator()
|
|
||||||
{
|
|
||||||
_accumulator = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Append(string key, string value)
|
public void Append(string key, string value)
|
||||||
{
|
{
|
||||||
|
if (_accumulator == null)
|
||||||
|
{
|
||||||
|
_accumulator = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
List<string> values;
|
List<string> values;
|
||||||
if (_accumulator.TryGetValue(key, out values))
|
if (_accumulator.TryGetValue(key, out values))
|
||||||
{
|
{
|
||||||
|
|
@ -25,18 +24,29 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_accumulator[key] = new List<string>(1) { value };
|
values = new List<string>(1);
|
||||||
|
values.Add(value);
|
||||||
|
_accumulator[key] = values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDictionary<string, StringValues> GetResults()
|
public bool HasValues => _accumulator != null;
|
||||||
|
|
||||||
|
public Dictionary<string, StringValues> GetResults()
|
||||||
{
|
{
|
||||||
var results = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
|
if (_accumulator == null)
|
||||||
|
{
|
||||||
|
return new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
var results = new Dictionary<string, StringValues>(_accumulator.Count, 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.Count == 1 ? new StringValues(kv.Value[0]) : new StringValues(kv.Value.ToArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ 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, StringValues>> ReadHeadersAsync(CancellationToken cancellationToken)
|
private async Task<Dictionary<string, StringValues>> ReadHeadersAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
int totalSize = 0;
|
int totalSize = 0;
|
||||||
var accumulator = new KeyValueAccumulator();
|
var accumulator = new KeyValueAccumulator();
|
||||||
|
|
@ -84,10 +84,10 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
totalSize += line.Length;
|
totalSize += line.Length;
|
||||||
if (totalSize > TotalHeaderSizeLimit)
|
if (totalSize > TotalHeaderSizeLimit)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Total header size limit exceeded: " + TotalHeaderSizeLimit);
|
throw new InvalidOperationException("Total header size limit exceeded: " + TotalHeaderSizeLimit.ToString());
|
||||||
}
|
}
|
||||||
int splitIndex = line.IndexOf(':');
|
int splitIndex = line.IndexOf(':');
|
||||||
Debug.Assert(splitIndex > 0, "Invalid header line: " + line);
|
Debug.Assert(splitIndex > 0, $"Invalid header line: {line.ToString()}");
|
||||||
if (splitIndex >= 0)
|
if (splitIndex >= 0)
|
||||||
{
|
{
|
||||||
var name = line.Substring(0, splitIndex);
|
var name = line.Substring(0, splitIndex);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
StringValues values;
|
StringValues values;
|
||||||
if (Headers.TryGetValue("Content-Type", out values))
|
if (Headers.TryGetValue("Content-Type", out values))
|
||||||
{
|
{
|
||||||
return values;
|
return values.ToString();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -29,13 +29,13 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
StringValues values;
|
StringValues values;
|
||||||
if (Headers.TryGetValue("Content-Disposition", out values))
|
if (Headers.TryGetValue("Content-Disposition", out values))
|
||||||
{
|
{
|
||||||
return values;
|
return values.ToString();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDictionary<string, StringValues> Headers { get; set; }
|
public Dictionary<string, StringValues> Headers { get; set; }
|
||||||
|
|
||||||
public Stream Body { get; set; }
|
public Stream Body { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,13 +107,39 @@ 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, StringValues> ParseQuery(string queryString)
|
public static Dictionary<string, StringValues> ParseQuery(string queryString)
|
||||||
{
|
{
|
||||||
|
var result = ParseNullableQuery(queryString);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return new Dictionary<string, StringValues>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a query string into its component key and value parts.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The raw query string value, with or without the leading '?'.</param>
|
||||||
|
/// <returns>A collection of parsed keys and values, null if there are no entries.</returns>
|
||||||
|
public static Dictionary<string, StringValues> ParseNullableQuery(string queryString)
|
||||||
|
{
|
||||||
|
var accumulator = new KeyValueAccumulator();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(queryString) || queryString == "?")
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scanIndex = 0;
|
||||||
if (!string.IsNullOrEmpty(queryString) && queryString[0] == '?')
|
if (!string.IsNullOrEmpty(queryString) && queryString[0] == '?')
|
||||||
{
|
{
|
||||||
queryString = queryString.Substring(1);
|
scanIndex = 1;
|
||||||
}
|
}
|
||||||
var accumulator = new KeyValueAccumulator();
|
|
||||||
|
|
||||||
int textLength = queryString.Length;
|
int textLength = queryString.Length;
|
||||||
int equalIndex = queryString.IndexOf('=');
|
int equalIndex = queryString.IndexOf('=');
|
||||||
|
|
@ -121,7 +147,6 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
{
|
{
|
||||||
equalIndex = textLength;
|
equalIndex = textLength;
|
||||||
}
|
}
|
||||||
int scanIndex = 0;
|
|
||||||
while (scanIndex < textLength)
|
while (scanIndex < textLength)
|
||||||
{
|
{
|
||||||
int delimiterIndex = queryString.IndexOf('&', scanIndex);
|
int delimiterIndex = queryString.IndexOf('&', scanIndex);
|
||||||
|
|
@ -149,6 +174,11 @@ namespace Microsoft.AspNet.WebUtilities
|
||||||
scanIndex = delimiterIndex + 1;
|
scanIndex = delimiterIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!accumulator.HasValues)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return accumulator.GetResults();
|
return accumulator.GetResults();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ 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, StringValues>()
|
var query2 = new QueryCollection( new Dictionary<string, StringValues>()
|
||||||
{
|
{
|
||||||
{ "name2", "value2" }
|
{ "name2", "value2" }
|
||||||
});
|
});
|
||||||
|
|
@ -170,19 +170,23 @@ namespace Microsoft.AspNet.Http.Internal
|
||||||
var cookies0 = request.Cookies;
|
var cookies0 = request.Cookies;
|
||||||
Assert.Equal(0, cookies0.Count);
|
Assert.Equal(0, cookies0.Count);
|
||||||
|
|
||||||
request.Headers["Cookie"] = new[] { "name0=value0", "name1=value1" };
|
var newCookies = new[] { "name0=value0", "name1=value1" };
|
||||||
|
request.Headers["Cookie"] = newCookies;
|
||||||
|
|
||||||
|
cookies0 = RequestCookieCollection.Parse(newCookies);
|
||||||
var cookies1 = request.Cookies;
|
var cookies1 = request.Cookies;
|
||||||
Assert.Same(cookies0, cookies1);
|
Assert.Equal(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"]);
|
||||||
|
Assert.Equal(newCookies, request.Headers["Cookie"]);
|
||||||
|
|
||||||
var cookies2 = new ReadableStringCollection(new Dictionary<string, StringValues>()
|
var cookies2 = new RequestCookieCollection(new Dictionary<string,string>()
|
||||||
{
|
{
|
||||||
{ "name2", "value2" }
|
{ "name2", "value2" }
|
||||||
});
|
});
|
||||||
request.Cookies = cookies2;
|
request.Cookies = cookies2;
|
||||||
Assert.Same(cookies2, request.Cookies);
|
Assert.Equal(cookies2, request.Cookies);
|
||||||
Assert.Equal("value2", request.Cookies["name2"]);
|
Assert.Equal("value2", request.Cookies["name2"]);
|
||||||
cookieHeaders = request.Headers["Cookie"];
|
cookieHeaders = request.Headers["Cookie"];
|
||||||
Assert.Equal(new[] { "name2=value2" }, cookieHeaders);
|
Assert.Equal(new[] { "name2=value2" }, cookieHeaders);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue