Roughing out more abstractions

This commit is contained in:
Louis DeJardin 2014-01-24 12:37:47 -08:00
parent 724897d0eb
commit f7a4db4ae1
19 changed files with 2048 additions and 16 deletions

View File

@ -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

View File

@ -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; }
}
}

View File

@ -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);
}
}
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,9 @@
namespace Microsoft.AspNet.Abstractions
{
/// <summary>
/// Contains the parsed form values.
/// </summary>
public interface IFormCollection : IReadableStringCollection
{
}
}

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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
{

View File

@ -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)
{
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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),
});
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -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";
}
}
}

View File

@ -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);
}
}
}

View File

@ -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")]

View File

@ -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>