Roughing out more abstractions
This commit is contained in:
parent
724897d0eb
commit
f7a4db4ae1
|
|
@ -21,11 +21,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.PipelineCo
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.PipelineCore.k10", "src\Microsoft.AspNet.PipelineCore\Microsoft.AspNet.PipelineCore.k10.csproj", "{E31CF247-FDA9-4007-B194-A7DBAC18532C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D38DDB2B-1138-4F45-8A6A-9499E880F620}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{1D737C82-F2F1-40B6-AE95-A3D878612E91}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.PipelineCore.Tests", "test\Microsoft.AspNet.PipelineCore.Tests\Microsoft.AspNet.PipelineCore.Tests.csproj", "{86942914-0334-4352-87ED-B971281C74E2}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.FeatureModel.Tests", "test\Microsoft.AspNet.FeatureModel.Tests\Microsoft.AspNet.FeatureModel.Tests.csproj", "{8C671AE3-1188-499F-A2A2-3A0B117B33CE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Abstractions.Owin.net45", "src\Microsoft.AspNet.Abstractions.Owin\Microsoft.AspNet.Abstractions.Owin.net45.csproj", "{E6B7056F-547E-4B96-9E58-858DDDAE75D3}"
|
||||
EndProject
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Abstractions
|
||||
{
|
||||
/// <summary>
|
||||
/// Options used to create a new cookie.
|
||||
/// </summary>
|
||||
public class CookieOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a default cookie with a path of '/'.
|
||||
/// </summary>
|
||||
public CookieOptions()
|
||||
{
|
||||
Path = "/";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the domain to associate the cookie with.
|
||||
/// </summary>
|
||||
/// <returns>The domain to associate the cookie with.</returns>
|
||||
public string Domain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cookie path.
|
||||
/// </summary>
|
||||
/// <returns>The cookie path.</returns>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expiration date and time for the cookie.
|
||||
/// </summary>
|
||||
/// <returns>The expiration date and time for the cookie.</returns>
|
||||
public DateTime? Expires { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether to transmit the cookie using Secure Sockets Layer (SSL)—that is, over HTTPS only.
|
||||
/// </summary>
|
||||
/// <returns>true to transmit the cookie only over an SSL connection (HTTPS); otherwise, false.</returns>
|
||||
public bool Secure { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether a cookie is accessible by client-side script.
|
||||
/// </summary>
|
||||
/// <returns>true if a cookie is accessible by client-side script; otherwise, false.</returns>
|
||||
public bool HttpOnly { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.AspNet.Abstractions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the host portion of a Uri can be used to construct Uri's properly formatted and encoded for use in
|
||||
/// HTTP headers.
|
||||
/// </summary>
|
||||
public struct HostString : IEquatable<HostString>
|
||||
{
|
||||
private readonly string _value;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new HostString without modification. The value should be Unicode rather than punycode, and may have a port.
|
||||
/// IPv4 and IPv6 addresses are also allowed, and also may have ports.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public HostString(string value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the original value from the constructor.
|
||||
/// </summary>
|
||||
public string Value
|
||||
{
|
||||
get { return _value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value as normalized by ToUriComponent().
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return ToUriComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value properly formatted and encoded for use in a URI in a HTTP header.
|
||||
/// Any Unicode is converted to punycode. IPv6 addresses will have brackets added if they are missing.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "Only the host segment of a uri is returned.")]
|
||||
public string ToUriComponent()
|
||||
{
|
||||
int index;
|
||||
if (string.IsNullOrEmpty(_value))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
else if (_value.IndexOf('[') >= 0)
|
||||
{
|
||||
// IPv6 in brackets [::1], maybe with port
|
||||
return _value;
|
||||
}
|
||||
else if ((index = _value.IndexOf(':')) >= 0
|
||||
&& index < _value.Length - 1
|
||||
&& _value.IndexOf(':', index + 1) >= 0)
|
||||
{
|
||||
// IPv6 without brackets ::1 is the only type of host with 2 or more colons
|
||||
return "[" + _value + "]";
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
// Has a port
|
||||
string port = _value.Substring(index);
|
||||
IdnMapping mapping = new IdnMapping();
|
||||
return mapping.GetAscii(_value, 0, index) + port;
|
||||
}
|
||||
else
|
||||
{
|
||||
IdnMapping mapping = new IdnMapping();
|
||||
return mapping.GetAscii(_value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new HostString from the given uri component.
|
||||
/// Any punycode will be converted to Unicode.
|
||||
/// </summary>
|
||||
/// <param name="uriComponent"></param>
|
||||
/// <returns></returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "Only the host segment of a uri is provided.")]
|
||||
public static HostString FromUriComponent(string uriComponent)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(uriComponent))
|
||||
{
|
||||
int index;
|
||||
if (uriComponent.IndexOf('[') >= 0)
|
||||
{
|
||||
// IPv6 in brackets [::1], maybe with port
|
||||
}
|
||||
else if ((index = uriComponent.IndexOf(':')) >= 0
|
||||
&& index < uriComponent.Length - 1
|
||||
&& uriComponent.IndexOf(':', index + 1) >= 0)
|
||||
{
|
||||
// IPv6 without brackets ::1 is the only type of host with 2 or more colons
|
||||
}
|
||||
else if (uriComponent.IndexOf("xn--", StringComparison.Ordinal) >= 0)
|
||||
{
|
||||
// Contains punycode
|
||||
if (index >= 0)
|
||||
{
|
||||
// Has a port
|
||||
string port = uriComponent.Substring(index);
|
||||
IdnMapping mapping = new IdnMapping();
|
||||
uriComponent = mapping.GetUnicode(uriComponent, 0, index) + port;
|
||||
}
|
||||
else
|
||||
{
|
||||
IdnMapping mapping = new IdnMapping();
|
||||
uriComponent = mapping.GetUnicode(uriComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new HostString(uriComponent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new HostString from the host and port of the give Uri instance.
|
||||
/// Punycode will be converted to Unicode.
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
/// <returns></returns>
|
||||
public static HostString FromUriComponent(Uri uri)
|
||||
{
|
||||
if (uri == null)
|
||||
{
|
||||
throw new ArgumentNullException("uri");
|
||||
}
|
||||
return new HostString(uri.GetComponents(
|
||||
#if !NET40
|
||||
UriComponents.NormalizedHost | // Always convert punycode to Unicode.
|
||||
#endif
|
||||
UriComponents.HostAndPort, UriFormat.Unescaped));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the equality of the Value property, ignoring case.
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public bool Equals(HostString other)
|
||||
{
|
||||
return string.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares against the given object only if it is a HostString.
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return obj is HostString && Equals((HostString)obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a hash code for the value.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (_value != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(_value) : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the two instances for equality.
|
||||
/// </summary>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator ==(HostString left, HostString right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the two instances for inequality.
|
||||
/// </summary>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator !=(HostString left, HostString right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.AspNet.Abstractions
|
||||
{
|
||||
|
|
@ -9,10 +10,113 @@ namespace Microsoft.AspNet.Abstractions
|
|||
|
||||
public abstract HttpContext HttpContext { get; }
|
||||
|
||||
public abstract Uri Uri { get; }
|
||||
/// <summary>
|
||||
/// Gets or set the HTTP method.
|
||||
/// </summary>
|
||||
/// <returns>The HTTP method.</returns>
|
||||
public abstract string Method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the HTTP request scheme from owin.RequestScheme.
|
||||
/// </summary>
|
||||
/// <returns>The HTTP request scheme from owin.RequestScheme.</returns>
|
||||
public abstract string Scheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the owin.RequestScheme is https.
|
||||
/// </summary>
|
||||
/// <returns>true if this request is using https; otherwise, false.</returns>
|
||||
public abstract bool IsSecure { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the Host header. May include the port.
|
||||
/// </summary>
|
||||
/// <return>The Host header.</return>
|
||||
public abstract HostString Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the owin.RequestPathBase.
|
||||
/// </summary>
|
||||
/// <returns>The owin.RequestPathBase.</returns>
|
||||
public abstract PathString PathBase { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the request path from owin.RequestPath.
|
||||
/// </summary>
|
||||
/// <returns>The request path from owin.RequestPath.</returns>
|
||||
public abstract PathString Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the query string from owin.RequestQueryString.
|
||||
/// </summary>
|
||||
/// <returns>The query string from owin.RequestQueryString.</returns>
|
||||
public abstract QueryString QueryString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the query value collection parsed from owin.RequestQueryString.
|
||||
/// </summary>
|
||||
/// <returns>The query value collection parsed from owin.RequestQueryString.</returns>
|
||||
public abstract IReadableStringCollection Query { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the uniform resource identifier (URI) associated with the request.
|
||||
/// </summary>
|
||||
/// <returns>The uniform resource identifier (URI) associated with the request.</returns>
|
||||
public abstract Uri Uri { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the owin.RequestProtocol.
|
||||
/// </summary>
|
||||
/// <returns>The owin.RequestProtocol.</returns>
|
||||
public abstract string Protocol { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request headers.
|
||||
/// </summary>
|
||||
/// <returns>The request headers.</returns>
|
||||
public abstract IHeaderDictionary Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of Cookies for this request.
|
||||
/// </summary>
|
||||
/// <returns>The collection of Cookies for this request.</returns>
|
||||
public abstract IReadableStringCollection Cookies { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Content-Type header.
|
||||
/// </summary>
|
||||
/// <returns>The Content-Type header.</returns>
|
||||
// (TODO header conventions?) public abstract string ContentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Cache-Control header.
|
||||
/// </summary>
|
||||
/// <returns>The Cache-Control header.</returns>
|
||||
// (TODO header conventions?) public abstract string CacheControl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Media-Type header.
|
||||
/// </summary>
|
||||
/// <returns>The Media-Type header.</returns>
|
||||
// (TODO header conventions?) public abstract string MediaType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the Accept header.
|
||||
/// </summary>
|
||||
/// <returns>The Accept header.</returns>
|
||||
// (TODO header conventions?) public abstract string Accept { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the owin.RequestBody Stream.
|
||||
/// </summary>
|
||||
/// <returns>The owin.RequestBody Stream.</returns>
|
||||
public abstract Stream Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cancellation token for the request.
|
||||
/// </summary>
|
||||
/// <returns>The cancellation token for the request.</returns>
|
||||
public abstract CancellationToken CallCanceled { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
namespace Microsoft.AspNet.Abstractions
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the parsed form values.
|
||||
/// </summary>
|
||||
public interface IFormCollection : IReadableStringCollection
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNet.Abstractions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents request and response headers
|
||||
/// </summary>
|
||||
public interface IHeaderDictionary : IReadableStringCollection, IDictionary<string, string[]>
|
||||
{
|
||||
/// <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 single string or null if the key is not present.</returns>
|
||||
new string this[string key] { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the associated values from the collection separated into individual values.
|
||||
/// Quoted values will not be split, and the quotes will be removed.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <returns>the associated values from the collection separated into individual values, or null if the key is not present.</returns>
|
||||
IList<string> GetCommaSeparatedValues(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new value. Appends to the header if already present
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="value">The header value.</param>
|
||||
void Append(string key, string value);
|
||||
|
||||
/// <summary>
|
||||
/// Add new values. Each item remains a separate array entry.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="values">The header values.</param>
|
||||
void AppendValues(string key, params string[] values);
|
||||
|
||||
/// <summary>
|
||||
/// Quotes any values containing comas, and then coma joins all of the values with any existing values.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="values">The header values.</param>
|
||||
void AppendCommaSeparatedValues(string key, params string[] values);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a specific header value.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="value">The header value.</param>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Set", Justification = "Re-evaluate later.")]
|
||||
void Set(string key, string value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified header values without modification.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="values">The header values.</param>
|
||||
void SetValues(string key, params string[] values);
|
||||
|
||||
/// <summary>
|
||||
/// Quotes any values containing comas, and then coma joins all of the values.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="values">The header values.</param>
|
||||
void SetCommaSeparatedValues(string key, params string[] values);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNet.Abstractions
|
||||
{
|
||||
/// <summary>
|
||||
/// Accessors for headers, query, forms, etc.
|
||||
/// </summary>
|
||||
public interface IReadableStringCollection : IEnumerable<KeyValuePair<string, string[]>>
|
||||
{
|
||||
/// <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>
|
||||
string this[string key] { get; }
|
||||
|
||||
// Joined
|
||||
|
||||
/// <summary>
|
||||
/// Get the associated value from the collection. Multiple values will be merged.
|
||||
/// Returns null if the key is not present.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "Re-evaluate later.")]
|
||||
string Get(string key);
|
||||
|
||||
// Joined
|
||||
|
||||
/// <summary>
|
||||
/// Get the associated values from the collection in their original format.
|
||||
/// Returns null if the key is not present.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
IList<string> GetValues(string key);
|
||||
|
||||
// Raw
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Abstractions
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Abstractions
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
using Microsoft.AspNet.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.PipelineCore.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the parsed form values.
|
||||
/// </summary>
|
||||
public class FormCollection : ReadableStringCollection, IFormCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:Microsoft.Owin.FormCollection" /> class.
|
||||
/// </summary>
|
||||
/// <param name="store">The store for the form.</param>
|
||||
public FormCollection(IDictionary<string, string[]> store)
|
||||
: base(store)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Abstractions.Infrastructure;
|
||||
using Microsoft.AspNet.Abstractions;
|
||||
using Microsoft.AspNet.PipelineCore.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNet.PipelineCore.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a wrapper for owin.RequestHeaders and owin.ResponseHeaders.
|
||||
/// </summary>
|
||||
public class HeaderDictionary : IHeaderDictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 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, string[]> store)
|
||||
{
|
||||
if (store == null)
|
||||
{
|
||||
throw new ArgumentNullException("store");
|
||||
}
|
||||
|
||||
Store = store;
|
||||
}
|
||||
|
||||
private IDictionary<string, string[]> Store { get; set; }
|
||||
|
||||
/// <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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public ICollection<string[]> 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>
|
||||
/// 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 single string or null if the key is not present.</returns>
|
||||
public string this[string key]
|
||||
{
|
||||
get { return Get(key); }
|
||||
set { Set(key, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws KeyNotFoundException if the key is not present.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <returns></returns>
|
||||
string[] IDictionary<string, string[]>.this[string key]
|
||||
{
|
||||
get { return Store[key]; }
|
||||
set { Store[key] = 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 IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
|
||||
{
|
||||
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()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the associated value from the collection as a single string.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <returns>the associated value from the collection as a single string or null if the key is not present.</returns>
|
||||
public string Get(string key)
|
||||
{
|
||||
return ParsingHelpers.GetHeader(Store, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the associated values from the collection without modification.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <returns>the associated value from the collection without modification, or null if the key is not present.</returns>
|
||||
public IList<string> GetValues(string key)
|
||||
{
|
||||
return ParsingHelpers.GetHeaderUnmodified(Store, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the associated values from the collection separated into individual values.
|
||||
/// Quoted values will not be split, and the quotes will be removed.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <returns>the associated values from the collection separated into individual values, or null if the key is not present.</returns>
|
||||
public IList<string> GetCommaSeparatedValues(string key)
|
||||
{
|
||||
IEnumerable<string> values = ParsingHelpers.GetHeaderSplit(Store, key);
|
||||
return values == null ? null : values.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new value. Appends to the header if already present
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="value">The header value.</param>
|
||||
public void Append(string key, string value)
|
||||
{
|
||||
ParsingHelpers.AppendHeader(Store, key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add new values. Each item remains a separate array entry.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="values">The header values.</param>
|
||||
public void AppendValues(string key, params string[] values)
|
||||
{
|
||||
ParsingHelpers.AppendHeaderUnmodified(Store, key, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quotes any values containing comas, and then coma joins all of the values with any existing values.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="values">The header values.</param>
|
||||
public void AppendCommaSeparatedValues(string key, params string[] values)
|
||||
{
|
||||
ParsingHelpers.AppendHeaderJoined(Store, key, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a specific header value.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="value">The header value.</param>
|
||||
public void Set(string key, string value)
|
||||
{
|
||||
ParsingHelpers.SetHeader(Store, key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified header values without modification.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="values">The header values.</param>
|
||||
public void SetValues(string key, params string[] values)
|
||||
{
|
||||
ParsingHelpers.SetHeaderUnmodified(Store, key, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quotes any values containing comas, and then coma joins all of the values.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="values">The header values.</param>
|
||||
public void SetCommaSeparatedValues(string key, params string[] values)
|
||||
{
|
||||
ParsingHelpers.SetHeaderJoined(Store, key, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given header and values to the collection.
|
||||
/// </summary>
|
||||
/// <param name="key">The header name.</param>
|
||||
/// <param name="value">The header values.</param>
|
||||
public void Add(string key, string[] 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 string[] 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, string[]> item)
|
||||
{
|
||||
Store.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the entire list of objects.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Store.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the specified object occurs within this collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>true if the specified object occurs within this collection; otherwise, false.</returns>
|
||||
public bool Contains(KeyValuePair<string, string[]> item)
|
||||
{
|
||||
return Store.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the <see cref="T:Microsoft.Owin.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.Owin.HeaderDictionary" />.</param>
|
||||
/// <param name="arrayIndex">The zero-based index in <paramref name="array" /> at which copying begins.</param>
|
||||
public void CopyTo(KeyValuePair<string, string[]>[] array, int arrayIndex)
|
||||
{
|
||||
Store.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given item from the the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
|
||||
public bool Remove(KeyValuePair<string, string[]> item)
|
||||
{
|
||||
return Store.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Abstractions.Infrastructure;
|
||||
using Microsoft.AspNet.Abstractions;
|
||||
using Microsoft.AspNet.PipelineCore.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNet.PipelineCore.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Accessors for query, forms, etc.
|
||||
/// </summary>
|
||||
public class ReadableStringCollection : IReadableStringCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new wrapper
|
||||
/// </summary>
|
||||
/// <param name="store"></param>
|
||||
public ReadableStringCollection(IDictionary<string, string[]> store)
|
||||
{
|
||||
if (store == null)
|
||||
{
|
||||
throw new ArgumentNullException("store");
|
||||
}
|
||||
|
||||
Store = store;
|
||||
}
|
||||
|
||||
private IDictionary<string, string[]> Store { get; set; }
|
||||
|
||||
/// <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 this[string key]
|
||||
{
|
||||
get { return Get(key); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the associated value from the collection. Multiple values will be merged.
|
||||
/// Returns null if the key is not present.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public string Get(string key)
|
||||
{
|
||||
return ParsingHelpers.GetJoinedValue(Store, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the associated values from the collection in their original format.
|
||||
/// Returns null if the key is not present.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public IList<string> GetValues(string key)
|
||||
{
|
||||
string[] values;
|
||||
Store.TryGetValue(key, out values);
|
||||
return values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
|
||||
{
|
||||
return Store.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.PipelineCore.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper for the request Cookie header
|
||||
/// </summary>
|
||||
public class RequestCookieCollection : IEnumerable<KeyValuePair<string, string>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new wrapper
|
||||
/// </summary>
|
||||
/// <param name="store"></param>
|
||||
public RequestCookieCollection(IDictionary<string, string> store)
|
||||
{
|
||||
if (store == null)
|
||||
{
|
||||
throw new ArgumentNullException("store");
|
||||
}
|
||||
|
||||
Store = store;
|
||||
}
|
||||
|
||||
private IDictionary<string, string> Store { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns null rather than throwing KeyNotFoundException
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
string value;
|
||||
Store.TryGetValue(key, out value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
|
||||
{
|
||||
return Store.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Abstractions.Infrastructure;
|
||||
using Microsoft.AspNet.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNet.PipelineCore.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper for the response Set-Cookie header
|
||||
/// </summary>
|
||||
public class ResponseCookieCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new wrapper
|
||||
/// </summary>
|
||||
/// <param name="headers"></param>
|
||||
public ResponseCookieCollection(IHeaderDictionary headers)
|
||||
{
|
||||
if (headers == null)
|
||||
{
|
||||
throw new ArgumentNullException("headers");
|
||||
}
|
||||
|
||||
Headers = headers;
|
||||
}
|
||||
|
||||
private IHeaderDictionary Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Add a new cookie and value
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
public void Append(string key, string value)
|
||||
{
|
||||
Headers.AppendValues(Constants.Headers.SetCookie, Uri.EscapeDataString(key) + "=" + Uri.EscapeDataString(value) + "; path=/");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new cookie
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="options"></param>
|
||||
public void Append(string key, string value, CookieOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException("options");
|
||||
}
|
||||
|
||||
bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
|
||||
bool pathHasValue = !string.IsNullOrEmpty(options.Path);
|
||||
bool expiresHasValue = options.Expires.HasValue;
|
||||
|
||||
string setCookieValue = string.Concat(
|
||||
Uri.EscapeDataString(key),
|
||||
"=",
|
||||
Uri.EscapeDataString(value ?? string.Empty),
|
||||
!domainHasValue ? null : "; domain=",
|
||||
!domainHasValue ? null : options.Domain,
|
||||
!pathHasValue ? null : "; path=",
|
||||
!pathHasValue ? null : options.Path,
|
||||
!expiresHasValue ? null : "; expires=",
|
||||
!expiresHasValue ? null : options.Expires.Value.ToString("ddd, dd-MMM-yyyy HH:mm:ss ", CultureInfo.InvariantCulture) + "GMT",
|
||||
!options.Secure ? null : "; secure",
|
||||
!options.HttpOnly ? null : "; HttpOnly");
|
||||
Headers.AppendValues("Set-Cookie", setCookieValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an expired cookie
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public void Delete(string key)
|
||||
{
|
||||
Func<string, bool> predicate = value => value.StartsWith(key + "=", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var deleteCookies = new[] { Uri.EscapeDataString(key) + "=; expires=Thu, 01-Jan-1970 00:00:00 GMT" };
|
||||
IList<string> existingValues = Headers.GetValues(Constants.Headers.SetCookie);
|
||||
if (existingValues == null || existingValues.Count == 0)
|
||||
{
|
||||
Headers.SetValues(Constants.Headers.SetCookie, deleteCookies);
|
||||
}
|
||||
else
|
||||
{
|
||||
Headers.SetValues(Constants.Headers.SetCookie, existingValues.Where(value => !predicate(value)).Concat(deleteCookies).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an expired cookie
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="options"></param>
|
||||
public void Delete(string key, CookieOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException("options");
|
||||
}
|
||||
|
||||
bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
|
||||
bool pathHasValue = !string.IsNullOrEmpty(options.Path);
|
||||
|
||||
Func<string, bool> rejectPredicate;
|
||||
if (domainHasValue)
|
||||
{
|
||||
rejectPredicate = value =>
|
||||
value.StartsWith(key + "=", StringComparison.OrdinalIgnoreCase) &&
|
||||
value.IndexOf("domain=" + options.Domain, StringComparison.OrdinalIgnoreCase) != -1;
|
||||
}
|
||||
else if (pathHasValue)
|
||||
{
|
||||
rejectPredicate = value =>
|
||||
value.StartsWith(key + "=", StringComparison.OrdinalIgnoreCase) &&
|
||||
value.IndexOf("path=" + options.Path, StringComparison.OrdinalIgnoreCase) != -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
rejectPredicate = value => value.StartsWith(key + "=", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
IList<string> existingValues = Headers.GetValues(Constants.Headers.SetCookie);
|
||||
if (existingValues != null)
|
||||
{
|
||||
Headers.SetValues(Constants.Headers.SetCookie, existingValues.Where(value => !rejectPredicate(value)).ToArray());
|
||||
}
|
||||
|
||||
Append(key, string.Empty, new CookieOptions
|
||||
{
|
||||
Path = options.Path,
|
||||
Domain = options.Domain,
|
||||
Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -70,5 +70,85 @@ namespace Microsoft.AspNet.PipelineCore
|
|||
get { return IHttpRequest.Body; }
|
||||
set { IHttpRequest.Body = value; }
|
||||
}
|
||||
|
||||
public override string Method
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override string Scheme
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsSecure
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override HostString Host
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override IReadableStringCollection Query
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override string Protocol
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override IHeaderDictionary Headers
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override IReadableStringCollection Cookies
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override System.Threading.CancellationToken CallCanceled
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNet.Abstractions.Infrastructure
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
internal const string Https = "HTTPS";
|
||||
|
||||
internal const string HttpDateFormat = "r";
|
||||
|
||||
internal static class Headers
|
||||
{
|
||||
internal const string ContentType = "Content-Type";
|
||||
internal const string CacheControl = "Cache-Control";
|
||||
internal const string MediaType = "Media-Type";
|
||||
internal const string Accept = "Accept";
|
||||
internal const string Host = "Host";
|
||||
internal const string ETag = "ETag";
|
||||
internal const string Location = "Location";
|
||||
internal const string ContentLength = "Content-Length";
|
||||
internal const string SetCookie = "Set-Cookie";
|
||||
internal const string Expires = "Expires";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,862 @@
|
|||
using Microsoft.AspNet.Abstractions;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.PipelineCore.Infrastructure
|
||||
{
|
||||
internal struct HeaderSegment : IEquatable<HeaderSegment>
|
||||
{
|
||||
private readonly StringSegment _formatting;
|
||||
private readonly StringSegment _data;
|
||||
|
||||
// <summary>
|
||||
// Initializes a new instance of the <see cref="T:System.Object"/> class.
|
||||
// </summary>
|
||||
public HeaderSegment(StringSegment formatting, StringSegment data)
|
||||
{
|
||||
_formatting = formatting;
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public StringSegment Formatting
|
||||
{
|
||||
get { return _formatting; }
|
||||
}
|
||||
|
||||
public StringSegment Data
|
||||
{
|
||||
get { return _data; }
|
||||
}
|
||||
|
||||
#region Equality members
|
||||
|
||||
public bool Equals(HeaderSegment other)
|
||||
{
|
||||
return _formatting.Equals(other._formatting) && _data.Equals(other._data);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj is HeaderSegment && Equals((HeaderSegment)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (_formatting.GetHashCode() * 397) ^ _data.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(HeaderSegment left, HeaderSegment right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(HeaderSegment left, HeaderSegment right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("App_Packages", "")]
|
||||
internal struct HeaderSegmentCollection : IEnumerable<HeaderSegment>, IEquatable<HeaderSegmentCollection>
|
||||
{
|
||||
private readonly string[] _headers;
|
||||
|
||||
public HeaderSegmentCollection(string[] headers)
|
||||
{
|
||||
_headers = headers;
|
||||
}
|
||||
|
||||
#region Equality members
|
||||
|
||||
public bool Equals(HeaderSegmentCollection other)
|
||||
{
|
||||
return Equals(_headers, other._headers);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj is HeaderSegmentCollection && Equals((HeaderSegmentCollection)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (_headers != null ? _headers.GetHashCode() : 0);
|
||||
}
|
||||
|
||||
public static bool operator ==(HeaderSegmentCollection left, HeaderSegmentCollection right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(HeaderSegmentCollection left, HeaderSegmentCollection right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(_headers);
|
||||
}
|
||||
|
||||
IEnumerator<HeaderSegment> IEnumerable<HeaderSegment>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
internal struct Enumerator : IEnumerator<HeaderSegment>
|
||||
{
|
||||
private readonly string[] _headers;
|
||||
private int _index;
|
||||
|
||||
private string _header;
|
||||
private int _headerLength;
|
||||
private int _offset;
|
||||
|
||||
private int _leadingStart;
|
||||
private int _leadingEnd;
|
||||
private int _valueStart;
|
||||
private int _valueEnd;
|
||||
private int _trailingStart;
|
||||
|
||||
private Mode _mode;
|
||||
|
||||
private static readonly string[] NoHeaders = new string[0];
|
||||
|
||||
public Enumerator(string[] headers)
|
||||
{
|
||||
_headers = headers ?? NoHeaders;
|
||||
_header = string.Empty;
|
||||
_headerLength = -1;
|
||||
_index = -1;
|
||||
_offset = -1;
|
||||
_leadingStart = -1;
|
||||
_leadingEnd = -1;
|
||||
_valueStart = -1;
|
||||
_valueEnd = -1;
|
||||
_trailingStart = -1;
|
||||
_mode = Mode.Leading;
|
||||
}
|
||||
|
||||
private enum Mode
|
||||
{
|
||||
Leading,
|
||||
Value,
|
||||
ValueQuoted,
|
||||
Trailing,
|
||||
Produce,
|
||||
}
|
||||
|
||||
private enum Attr
|
||||
{
|
||||
Value,
|
||||
Quote,
|
||||
Delimiter,
|
||||
Whitespace
|
||||
}
|
||||
|
||||
public HeaderSegment Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return new HeaderSegment(
|
||||
new StringSegment(_header, _leadingStart, _leadingEnd - _leadingStart),
|
||||
new StringSegment(_header, _valueStart, _valueEnd - _valueStart));
|
||||
}
|
||||
}
|
||||
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get { return Current; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_mode == Mode.Produce)
|
||||
{
|
||||
_leadingStart = _trailingStart;
|
||||
_leadingEnd = -1;
|
||||
_valueStart = -1;
|
||||
_valueEnd = -1;
|
||||
_trailingStart = -1;
|
||||
|
||||
if (_offset == _headerLength &&
|
||||
_leadingStart != -1 &&
|
||||
_leadingStart != _offset)
|
||||
{
|
||||
// Also produce trailing whitespace
|
||||
_leadingEnd = _offset;
|
||||
return true;
|
||||
}
|
||||
_mode = Mode.Leading;
|
||||
}
|
||||
|
||||
// if end of a string
|
||||
if (_offset == _headerLength)
|
||||
{
|
||||
++_index;
|
||||
_offset = -1;
|
||||
_leadingStart = 0;
|
||||
_leadingEnd = -1;
|
||||
_valueStart = -1;
|
||||
_valueEnd = -1;
|
||||
_trailingStart = -1;
|
||||
|
||||
// if that was the last string
|
||||
if (_index == _headers.Length)
|
||||
{
|
||||
// no more move nexts
|
||||
return false;
|
||||
}
|
||||
|
||||
// grab the next string
|
||||
_header = _headers[_index] ?? string.Empty;
|
||||
_headerLength = _header.Length;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
++_offset;
|
||||
char ch = _offset == _headerLength ? (char)0 : _header[_offset];
|
||||
// todo - array of attrs
|
||||
Attr attr = char.IsWhiteSpace(ch) ? Attr.Whitespace : ch == '\"' ? Attr.Quote : (ch == ',' || ch == (char)0) ? Attr.Delimiter : Attr.Value;
|
||||
|
||||
switch (_mode)
|
||||
{
|
||||
case Mode.Leading:
|
||||
switch (attr)
|
||||
{
|
||||
case Attr.Delimiter:
|
||||
_leadingEnd = _offset;
|
||||
_mode = Mode.Produce;
|
||||
break;
|
||||
case Attr.Quote:
|
||||
_leadingEnd = _offset;
|
||||
_valueStart = _offset;
|
||||
_mode = Mode.ValueQuoted;
|
||||
break;
|
||||
case Attr.Value:
|
||||
_leadingEnd = _offset;
|
||||
_valueStart = _offset;
|
||||
_mode = Mode.Value;
|
||||
break;
|
||||
case Attr.Whitespace:
|
||||
// more
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Mode.Value:
|
||||
switch (attr)
|
||||
{
|
||||
case Attr.Quote:
|
||||
_mode = Mode.ValueQuoted;
|
||||
break;
|
||||
case Attr.Delimiter:
|
||||
_valueEnd = _offset;
|
||||
_trailingStart = _offset;
|
||||
_mode = Mode.Produce;
|
||||
break;
|
||||
case Attr.Value:
|
||||
// more
|
||||
break;
|
||||
case Attr.Whitespace:
|
||||
_valueEnd = _offset;
|
||||
_trailingStart = _offset;
|
||||
_mode = Mode.Trailing;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Mode.ValueQuoted:
|
||||
switch (attr)
|
||||
{
|
||||
case Attr.Quote:
|
||||
_mode = Mode.Value;
|
||||
break;
|
||||
case Attr.Delimiter:
|
||||
if (ch == (char)0)
|
||||
{
|
||||
_valueEnd = _offset;
|
||||
_trailingStart = _offset;
|
||||
_mode = Mode.Produce;
|
||||
}
|
||||
break;
|
||||
case Attr.Value:
|
||||
case Attr.Whitespace:
|
||||
// more
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Mode.Trailing:
|
||||
switch (attr)
|
||||
{
|
||||
case Attr.Delimiter:
|
||||
_mode = Mode.Produce;
|
||||
break;
|
||||
case Attr.Quote:
|
||||
// back into value
|
||||
_trailingStart = -1;
|
||||
_valueEnd = -1;
|
||||
_mode = Mode.ValueQuoted;
|
||||
break;
|
||||
case Attr.Value:
|
||||
// back into value
|
||||
_trailingStart = -1;
|
||||
_valueEnd = -1;
|
||||
_mode = Mode.Value;
|
||||
break;
|
||||
case Attr.Whitespace:
|
||||
// more
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (_mode == Mode.Produce)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = 0;
|
||||
_offset = 0;
|
||||
_leadingStart = 0;
|
||||
_leadingEnd = 0;
|
||||
_valueStart = 0;
|
||||
_valueEnd = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("App_Packages", "")]
|
||||
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; }
|
||||
}
|
||||
|
||||
#region Equality members
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public bool StartsWith(string text, StringComparison comparisonType)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
throw new ArgumentNullException("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("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("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;
|
||||
}
|
||||
}
|
||||
|
||||
internal static partial class ParsingHelpers
|
||||
{
|
||||
private static readonly Action<string, string, object> AddCookieCallback = (name, value, state) =>
|
||||
{
|
||||
var dictionary = (IDictionary<string, string>)state;
|
||||
if (!dictionary.ContainsKey(name))
|
||||
{
|
||||
dictionary.Add(name, value);
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly char[] SemicolonAndComma = new[] { ';', ',' };
|
||||
|
||||
internal static IDictionary<string, string> GetCookies(HttpRequest request)
|
||||
{
|
||||
var cookies = request.Get<IDictionary<string, string>>("Microsoft.Owin.Cookies#dictionary");
|
||||
if (cookies == null)
|
||||
{
|
||||
cookies = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
request.Set("Microsoft.Owin.Cookies#dictionary", cookies);
|
||||
}
|
||||
|
||||
string text = GetHeader(request.Headers, "Cookie");
|
||||
if (request.Get<string>("Microsoft.Owin.Cookies#text") != text)
|
||||
{
|
||||
cookies.Clear();
|
||||
ParseDelimited(text, SemicolonAndComma, AddCookieCallback, cookies);
|
||||
request.Set("Microsoft.Owin.Cookies#text", text);
|
||||
}
|
||||
return cookies;
|
||||
}
|
||||
|
||||
internal static void ParseDelimited(string text, char[] delimiters, Action<string, string, object> callback, object state)
|
||||
{
|
||||
int textLength = text.Length;
|
||||
int equalIndex = text.IndexOf('=');
|
||||
if (equalIndex == -1)
|
||||
{
|
||||
equalIndex = textLength;
|
||||
}
|
||||
int scanIndex = 0;
|
||||
while (scanIndex < textLength)
|
||||
{
|
||||
int delimiterIndex = text.IndexOfAny(delimiters, scanIndex);
|
||||
if (delimiterIndex == -1)
|
||||
{
|
||||
delimiterIndex = textLength;
|
||||
}
|
||||
if (equalIndex < delimiterIndex)
|
||||
{
|
||||
while (scanIndex != equalIndex && char.IsWhiteSpace(text[scanIndex]))
|
||||
{
|
||||
++scanIndex;
|
||||
}
|
||||
string name = text.Substring(scanIndex, equalIndex - scanIndex);
|
||||
string value = text.Substring(equalIndex + 1, delimiterIndex - equalIndex - 1);
|
||||
callback(
|
||||
Uri.UnescapeDataString(name.Replace('+', ' ')),
|
||||
Uri.UnescapeDataString(value.Replace('+', ' ')),
|
||||
state);
|
||||
equalIndex = text.IndexOf('=', delimiterIndex);
|
||||
if (equalIndex == -1)
|
||||
{
|
||||
equalIndex = textLength;
|
||||
}
|
||||
}
|
||||
scanIndex = delimiterIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetJoinedValue(IDictionary<string, string[]> Store, string key)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
internal static partial class OwinHelpers
|
||||
{
|
||||
public static string GetHeader(IDictionary<string, string[]> headers, string key)
|
||||
{
|
||||
string[] values = GetHeaderUnmodified(headers, key);
|
||||
return values == null ? null : string.Join(",", values);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetHeaderSplit(IDictionary<string, string[]> headers, string key)
|
||||
{
|
||||
string[] values = GetHeaderUnmodified(headers, key);
|
||||
return values == null ? null : GetHeaderSplitImplementation(values);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetHeaderSplitImplementation(string[] values)
|
||||
{
|
||||
foreach (var segment in new HeaderSegmentCollection(values))
|
||||
{
|
||||
if (segment.Data.HasValue)
|
||||
{
|
||||
yield return DeQuote(segment.Data.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string[] GetHeaderUnmodified(IDictionary<string, string[]> headers, string key)
|
||||
{
|
||||
if (headers == null)
|
||||
{
|
||||
throw new ArgumentNullException("headers");
|
||||
}
|
||||
string[] values;
|
||||
return headers.TryGetValue(key, out values) ? values : null;
|
||||
}
|
||||
|
||||
public static void SetHeader(IDictionary<string, string[]> headers, string key, string value)
|
||||
{
|
||||
if (headers == null)
|
||||
{
|
||||
throw new ArgumentNullException("headers");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
headers.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
headers[key] = new[] { value };
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetHeaderJoined(IDictionary<string, string[]> headers, string key, params string[] values)
|
||||
{
|
||||
if (headers == null)
|
||||
{
|
||||
throw new ArgumentNullException("headers");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
if (values == null || values.Length == 0)
|
||||
{
|
||||
headers.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
headers[key] = new[] { string.Join(",", values.Select(value => QuoteIfNeeded(value))) };
|
||||
}
|
||||
}
|
||||
|
||||
// Quote items that contain comas and are not already quoted.
|
||||
private static string QuoteIfNeeded(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
else if (value.Contains(','))
|
||||
{
|
||||
if (value[0] != '"' || value[value.Length - 1] != '"')
|
||||
{
|
||||
value = '"' + value + '"';
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string DeQuote(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
else if (value.Length > 1 && value[0] == '"' && value[value.Length - 1] == '"')
|
||||
{
|
||||
value = value.Substring(1, value.Length - 2);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void SetHeaderUnmodified(IDictionary<string, string[]> headers, string key, params string[] values)
|
||||
{
|
||||
if (headers == null)
|
||||
{
|
||||
throw new ArgumentNullException("headers");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
if (values == null || values.Length == 0)
|
||||
{
|
||||
headers.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
headers[key] = values;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetHeaderUnmodified(IDictionary<string, string[]> headers, string key, IEnumerable<string> values)
|
||||
{
|
||||
if (headers == null)
|
||||
{
|
||||
throw new ArgumentNullException("headers");
|
||||
}
|
||||
headers[key] = values.ToArray();
|
||||
}
|
||||
|
||||
public static void AppendHeader(IDictionary<string, string[]> headers, string key, string values)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(values))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string existing = GetHeader(headers, key);
|
||||
if (existing == null)
|
||||
{
|
||||
SetHeader(headers, key, values);
|
||||
}
|
||||
else
|
||||
{
|
||||
headers[key] = new[] { existing + "," + values };
|
||||
}
|
||||
}
|
||||
|
||||
public static void AppendHeaderJoined(IDictionary<string, string[]> headers, string key, params string[] values)
|
||||
{
|
||||
if (values == null || values.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string existing = GetHeader(headers, key);
|
||||
if (existing == null)
|
||||
{
|
||||
SetHeaderJoined(headers, key, values);
|
||||
}
|
||||
else
|
||||
{
|
||||
headers[key] = new[] { existing + "," + string.Join(",", values.Select(value => QuoteIfNeeded(value))) };
|
||||
}
|
||||
}
|
||||
|
||||
public static void AppendHeaderUnmodified(IDictionary<string, string[]> headers, string key, params string[] values)
|
||||
{
|
||||
if (values == null || values.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] existing = GetHeaderUnmodified(headers, key);
|
||||
if (existing == null)
|
||||
{
|
||||
SetHeaderUnmodified(headers, key, values);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetHeaderUnmodified(headers, key, existing.Concat(values));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static partial class OwinHelpers
|
||||
{
|
||||
private static readonly Action<string, string, object> AppendItemCallback = (name, value, state) =>
|
||||
{
|
||||
var dictionary = (IDictionary<string, List<String>>)state;
|
||||
|
||||
List<string> existing;
|
||||
if (!dictionary.TryGetValue(name, out existing))
|
||||
{
|
||||
dictionary.Add(name, new List<string>(1) { value });
|
||||
}
|
||||
else
|
||||
{
|
||||
existing.Add(value);
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly char[] AmpersandAndSemicolon = new[] { '&', ';' };
|
||||
|
||||
internal static IDictionary<string, string[]> GetQuery(HttpRequest request)
|
||||
{
|
||||
var query = request.Get<IDictionary<string, string[]>>("Microsoft.Owin.Query#dictionary");
|
||||
if (query == null)
|
||||
{
|
||||
query = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
||||
request.Set("Microsoft.Owin.Query#dictionary", query);
|
||||
}
|
||||
|
||||
string text = request.QueryString.Value;
|
||||
if (request.Get<string>("Microsoft.Owin.Query#text") != text)
|
||||
{
|
||||
query.Clear();
|
||||
var accumulator = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
ParseDelimited(text, AmpersandAndSemicolon, AppendItemCallback, accumulator);
|
||||
foreach (var kv in accumulator)
|
||||
{
|
||||
query.Add(kv.Key, kv.Value.ToArray());
|
||||
}
|
||||
request.Set("Microsoft.Owin.Query#text", text);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
#if !NET40
|
||||
internal static IFormCollection GetForm(string text)
|
||||
{
|
||||
IDictionary<string, string[]> form = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
||||
var accumulator = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
ParseDelimited(text, new[] { '&' }, AppendItemCallback, accumulator);
|
||||
foreach (var kv in accumulator)
|
||||
{
|
||||
form.Add(kv.Key, kv.Value.ToArray());
|
||||
}
|
||||
return new FormCollection(form);
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static string GetJoinedValue(IDictionary<string, string[]> store, string key)
|
||||
{
|
||||
string[] values = GetUnmodifiedValues(store, key);
|
||||
return values == null ? null : string.Join(",", values);
|
||||
}
|
||||
|
||||
internal static string[] GetUnmodifiedValues(IDictionary<string, string[]> store, string key)
|
||||
{
|
||||
if (store == null)
|
||||
{
|
||||
throw new ArgumentNullException("store");
|
||||
}
|
||||
string[] values;
|
||||
return store.TryGetValue(key, out values) ? values : null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static partial class OwinHelpers
|
||||
{
|
||||
internal static string GetHost(HttpRequest request)
|
||||
{
|
||||
IHeaderDictionary headers = request.Headers;
|
||||
|
||||
string host = GetHeader(headers, "Host");
|
||||
if (!string.IsNullOrWhiteSpace(host))
|
||||
{
|
||||
return host;
|
||||
}
|
||||
|
||||
string localIpAddress = request.LocalIpAddress ?? "localhost";
|
||||
var localPort = request.Get<string>(OwinConstants.CommonKeys.LocalPort);
|
||||
return string.IsNullOrWhiteSpace(localPort) ? localIpAddress : (localIpAddress + ":" + localPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
|
@ -31,6 +31,6 @@ using System.Runtime.InteropServices;
|
|||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
// [assembly: AssemblyVersion("0.1.0")]
|
||||
[assembly: AssemblyVersion("0.1.0")]
|
||||
[assembly: AssemblyFileVersion("0.1.0")]
|
||||
|
|
|
|||
|
|
@ -53,6 +53,19 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BuilderTests.cs" />
|
||||
<Compile Include="Builder.cs" />
|
||||
<Compile Include="Collections\FormCollection.cs" />
|
||||
<Compile Include="Collections\HeaderDictionary.cs" />
|
||||
<Compile Include="Collections\ReadableStringCollection.cs" />
|
||||
<Compile Include="Collections\RequestCookieCollection.cs" />
|
||||
<Compile Include="Collections\ResponseCookieCollection.cs" />
|
||||
<Compile Include="DefaultHttpContext.cs" />
|
||||
<Compile Include="DefaultHttpRequest.cs" />
|
||||
<Compile Include="DefaultHttpResponse.cs" />
|
||||
<Compile Include="Infrastructure\Constants.cs" />
|
||||
<Compile Include="Infrastructure\ParsingHelpers.cs" />
|
||||
<Compile Include="Owin\OwinConstants.cs" />
|
||||
<Compile Include="Owin\OwinHttpEnvironment.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in New Issue