Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,131 @@
//
// AuthenticationHeaderValue.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
public class AuthenticationHeaderValue : ICloneable
{
public AuthenticationHeaderValue (string scheme)
: this (scheme, null)
{
}
public AuthenticationHeaderValue (string scheme, string parameter)
{
Parser.Token.Check (scheme);
this.Scheme = scheme;
this.Parameter = parameter;
}
private AuthenticationHeaderValue ()
{
}
public string Parameter { get; private set; }
public string Scheme { get; private set; }
object ICloneable.Clone ()
{
return MemberwiseClone ();
}
public override bool Equals (object obj)
{
var source = obj as AuthenticationHeaderValue;
return source != null &&
string.Equals (source.Scheme, Scheme, StringComparison.OrdinalIgnoreCase) &&
source.Parameter == Parameter;
}
public override int GetHashCode ()
{
int hc = Scheme.ToLowerInvariant ().GetHashCode ();
if (!string.IsNullOrEmpty (Parameter)) {
hc ^= Parameter.ToLowerInvariant ().GetHashCode ();
}
return hc;
}
public static AuthenticationHeaderValue Parse (string input)
{
AuthenticationHeaderValue value;
if (TryParse (input, out value))
return value;
throw new FormatException (input);
}
public static bool TryParse (string input, out AuthenticationHeaderValue parsedValue)
{
var lexer = new Lexer (input);
Token token;
if (TryParseElement (lexer, out parsedValue, out token) && token == Token.Type.End)
return true;
parsedValue = null;
return false;
}
internal static bool TryParse (string input, int minimalCount, out List<AuthenticationHeaderValue> result)
{
return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result);
}
static bool TryParseElement (Lexer lexer, out AuthenticationHeaderValue parsedValue, out Token t)
{
t = lexer.Scan ();
if (t != Token.Type.Token) {
parsedValue = null;
return false;
}
parsedValue = new AuthenticationHeaderValue ();
parsedValue.Scheme = lexer.GetStringValue (t);
t = lexer.Scan ();
if (t == Token.Type.Token) {
// TODO: Wrong with multi value parsing
parsedValue.Parameter = lexer.GetRemainingStringValue (t.StartPosition);
t = new Token (Token.Type.End, 0, 0);
}
return true;
}
public override string ToString ()
{
return Parameter != null ?
Scheme + " " + Parameter :
Scheme;
}
}
}

View File

@ -0,0 +1,403 @@
//
// CacheControlHeaderValue.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
using System.Text;
using System.Globalization;
namespace System.Net.Http.Headers
{
public class CacheControlHeaderValue : ICloneable
{
List<NameValueHeaderValue> extensions;
List<string> no_cache_headers, private_headers;
public ICollection<NameValueHeaderValue> Extensions {
get {
return extensions ?? (extensions = new List<NameValueHeaderValue> ());
}
}
public TimeSpan? MaxAge { get; set; }
public bool MaxStale { get; set; }
public TimeSpan? MaxStaleLimit { get; set; }
public TimeSpan? MinFresh { get; set; }
public bool MustRevalidate { get; set; }
public bool NoCache { get; set; }
public ICollection<string> NoCacheHeaders {
get {
return no_cache_headers ?? (no_cache_headers = new List<string> ());
}
}
public bool NoStore { get; set; }
public bool NoTransform { get; set; }
public bool OnlyIfCached { get; set; }
public bool Private { get; set; }
public ICollection<string> PrivateHeaders {
get {
return private_headers ?? (private_headers = new List<string> ());
}
}
public bool ProxyRevalidate { get; set; }
public bool Public { get; set; }
public TimeSpan? SharedMaxAge { get; set; }
object ICloneable.Clone ()
{
var copy = (CacheControlHeaderValue) MemberwiseClone ();
if (extensions != null) {
copy.extensions = new List<NameValueHeaderValue> ();
foreach (var entry in extensions) {
copy.extensions.Add (entry);
}
}
if (no_cache_headers != null) {
copy.no_cache_headers = new List<string> ();
foreach (var entry in no_cache_headers) {
copy.no_cache_headers.Add (entry);
}
}
if (private_headers != null) {
copy.private_headers = new List<string> ();
foreach (var entry in private_headers) {
copy.private_headers.Add (entry);
}
}
return copy;
}
public override bool Equals (object obj)
{
var source = obj as CacheControlHeaderValue;
if (source == null)
return false;
if (MaxAge != source.MaxAge || MaxStale != source.MaxStale || MaxStaleLimit != source.MaxStaleLimit ||
MinFresh != source.MinFresh || MustRevalidate != source.MustRevalidate || NoCache != source.NoCache ||
NoStore != source.NoStore || NoTransform != source.NoTransform || OnlyIfCached != source.OnlyIfCached ||
Private != source.Private || ProxyRevalidate != source.ProxyRevalidate || Public != source.Public ||
SharedMaxAge != source.SharedMaxAge)
return false;
return extensions.SequenceEqual (source.extensions) &&
no_cache_headers.SequenceEqual (source.no_cache_headers) &&
private_headers.SequenceEqual (source.private_headers);
}
public override int GetHashCode ()
{
int hc = 29;
unchecked {
hc = hc * 29 + HashCodeCalculator.Calculate (extensions);
hc = hc * 29 + MaxAge.GetHashCode ();
hc = hc * 29 + MaxStale.GetHashCode ();
hc = hc * 29 + MaxStaleLimit.GetHashCode ();
hc = hc * 29 + MinFresh.GetHashCode ();
hc = hc * 29 + MustRevalidate.GetHashCode ();
hc = hc * 29 + HashCodeCalculator.Calculate (no_cache_headers);
hc = hc * 29 + NoCache.GetHashCode ();
hc = hc * 29 + NoStore.GetHashCode ();
hc = hc * 29 + NoTransform.GetHashCode ();
hc = hc * 29 + OnlyIfCached.GetHashCode ();
hc = hc * 29 + Private.GetHashCode ();
hc = hc * 29 + HashCodeCalculator.Calculate (private_headers);
hc = hc * 29 + ProxyRevalidate.GetHashCode ();
hc = hc * 29 + Public.GetHashCode ();
hc = hc * 29 + SharedMaxAge.GetHashCode ();
}
return hc;
}
public static CacheControlHeaderValue Parse (string input)
{
CacheControlHeaderValue value;
if (TryParse (input, out value))
return value;
throw new FormatException (input);
}
public static bool TryParse (string input, out CacheControlHeaderValue parsedValue)
{
parsedValue = null;
if (input == null)
return true;
var value = new CacheControlHeaderValue ();
var lexer = new Lexer (input);
Token t;
do {
t = lexer.Scan ();
if (t != Token.Type.Token)
return false;
string s = lexer.GetStringValue (t);
bool token_read = false;
TimeSpan? ts;
switch (s) {
case "no-store":
value.NoStore = true;
break;
case "no-transform":
value.NoTransform = true;
break;
case "only-if-cached":
value.OnlyIfCached = true;
break;
case "public":
value.Public = true;
break;
case "must-revalidate":
value.MustRevalidate = true;
break;
case "proxy-revalidate":
value.ProxyRevalidate = true;
break;
case "max-stale":
value.MaxStale = true;
t = lexer.Scan ();
if (t != Token.Type.SeparatorEqual) {
token_read = true;
break;
}
t = lexer.Scan ();
if (t != Token.Type.Token)
return false;
ts = lexer.TryGetTimeSpanValue (t);
if (ts == null)
return false;
value.MaxStaleLimit = ts;
break;
case "max-age":
case "s-maxage":
case "min-fresh":
t = lexer.Scan ();
if (t != Token.Type.SeparatorEqual) {
return false;
}
t = lexer.Scan ();
if (t != Token.Type.Token)
return false;
ts = lexer.TryGetTimeSpanValue (t);
if (ts == null)
return false;
switch (s.Length) {
case 7:
value.MaxAge = ts;
break;
case 8:
value.SharedMaxAge = ts;
break;
default:
value.MinFresh = ts;
break;
}
break;
case "private":
case "no-cache":
if (s.Length == 7) {
value.Private = true;
} else {
value.NoCache = true;
}
t = lexer.Scan ();
if (t != Token.Type.SeparatorEqual) {
token_read = true;
break;
}
t = lexer.Scan ();
if (t != Token.Type.QuotedString)
return false;
foreach (var entry in lexer.GetQuotedStringValue (t).Split (',')) {
var qs = entry.Trim ('\t', ' ');
if (s.Length == 7) {
value.PrivateHeaders.Add (qs);
} else {
value.NoCache = true;
value.NoCacheHeaders.Add (qs);
}
}
break;
default:
string name = lexer.GetStringValue (t);
string svalue = null;
t = lexer.Scan ();
if (t == Token.Type.SeparatorEqual) {
t = lexer.Scan ();
switch (t.Kind) {
case Token.Type.Token:
case Token.Type.QuotedString:
svalue = lexer.GetStringValue (t);
break;
default:
return false;
}
} else {
token_read = true;
}
value.Extensions.Add (NameValueHeaderValue.Create (name, svalue));
break;
}
if (!token_read)
t = lexer.Scan ();
} while (t == Token.Type.SeparatorComma);
if (t != Token.Type.End)
return false;
parsedValue = value;
return true;
}
public override string ToString ()
{
const string separator = ", ";
var sb = new StringBuilder ();
if (NoStore) {
sb.Append ("no-store");
sb.Append (separator);
}
if (NoTransform) {
sb.Append ("no-transform");
sb.Append (separator);
}
if (OnlyIfCached) {
sb.Append ("only-if-cached");
sb.Append (separator);
}
if (Public) {
sb.Append ("public");
sb.Append (separator);
}
if (MustRevalidate) {
sb.Append ("must-revalidate");
sb.Append (separator);
}
if (ProxyRevalidate) {
sb.Append ("proxy-revalidate");
sb.Append (separator);
}
if (NoCache) {
sb.Append ("no-cache");
if (no_cache_headers != null) {
sb.Append ("=\"");
no_cache_headers.ToStringBuilder (sb);
sb.Append ("\"");
}
sb.Append (separator);
}
if (MaxAge != null) {
sb.Append ("max-age=");
sb.Append (MaxAge.Value.TotalSeconds.ToString (CultureInfo.InvariantCulture));
sb.Append (separator);
}
if (SharedMaxAge != null) {
sb.Append ("s-maxage=");
sb.Append (SharedMaxAge.Value.TotalSeconds.ToString (CultureInfo.InvariantCulture));
sb.Append (separator);
}
if (MaxStale) {
sb.Append ("max-stale");
if (MaxStaleLimit != null) {
sb.Append ("=");
sb.Append (MaxStaleLimit.Value.TotalSeconds.ToString (CultureInfo.InvariantCulture));
}
sb.Append (separator);
}
if (MinFresh != null) {
sb.Append ("min-fresh=");
sb.Append (MinFresh.Value.TotalSeconds.ToString (CultureInfo.InvariantCulture));
sb.Append (separator);
}
if (Private) {
sb.Append ("private");
if (private_headers != null) {
sb.Append ("=\"");
private_headers.ToStringBuilder (sb);
sb.Append ("\"");
}
sb.Append (separator);
}
CollectionExtensions.ToStringBuilder (extensions, sb);
if (sb.Length > 2 && sb[sb.Length - 2] == ',' && sb[sb.Length - 1] == ' ')
sb.Remove (sb.Length - 2, 2);
return sb.ToString ();
}
}
}

View File

@ -0,0 +1,100 @@
//
// CollectionExtensions.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace System.Net.Http.Headers
{
static class CollectionExtensions
{
public static bool SequenceEqual<TSource> (this List<TSource> first, List<TSource> second)
{
if (first == null)
return second == null || second.Count == 0;
if (second == null)
return first == null || first.Count == 0;
return Enumerable.SequenceEqual (first, second);
}
public static void SetValue (this List<NameValueHeaderValue> parameters, string key, string value)
{
for (int i = 0; i < parameters.Count; ++i) {
var entry = parameters[i];
if (!string.Equals (entry.Name, key, StringComparison.OrdinalIgnoreCase))
continue;
if (value == null) {
parameters.RemoveAt (i);
} else {
parameters[i].Value = value;
}
return;
}
if (!string.IsNullOrEmpty (value))
parameters.Add (new NameValueHeaderValue (key, value));
}
public static string ToString<T> (this List<T> list)
{
if (list == null || list.Count == 0)
return null;
const string separator = "; ";
var sb = new StringBuilder ();
for (int i = 0; i < list.Count; ++i) {
sb.Append (separator);
sb.Append (list [i]);
}
return sb.ToString ();
}
public static void ToStringBuilder<T> (this List<T> list, StringBuilder sb)
{
if (list == null || list.Count == 0)
return;
const string separator = ", ";
for (int i = 0; i < list.Count; ++i) {
if (i > 0) {
sb.Append (separator);
}
sb.Append (list[i]);
}
}
}
}

View File

@ -0,0 +1,117 @@
//
// CollectionParser.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
delegate bool ElementTryParser<T> (Lexer lexer, out T parsedValue, out Token token);
static class CollectionParser
{
public static bool TryParse<T> (string input, int minimalCount, ElementTryParser<T> parser, out List<T> result) where T : class
{
var lexer = new Lexer (input);
result = new List<T> ();
while (true) {
Token token;
T parsedValue;
if (!parser (lexer, out parsedValue, out token))
return false;
if (parsedValue != null)
result.Add (parsedValue);
if (token == Token.Type.SeparatorComma)
continue;
if (token == Token.Type.End) {
if (minimalCount > result.Count) {
result = null;
return false;
}
return true;
}
result = null;
return false;
}
}
public static bool TryParse (string input, int minimalCount, out List<string> result)
{
return TryParse (input, minimalCount, TryParseStringElement, out result);
}
public static bool TryParseRepetition (string input, int minimalCount, out List<string> result)
{
return TryParseRepetition (input, minimalCount, TryParseStringElement, out result);
}
static bool TryParseStringElement (Lexer lexer, out string parsedValue, out Token t)
{
t = lexer.Scan ();
if (t == Token.Type.Token) {
parsedValue = lexer.GetStringValue (t);
if (parsedValue.Length == 0)
parsedValue = null;
t = lexer.Scan ();
} else {
parsedValue = null;
}
return true;
}
public static bool TryParseRepetition<T> (string input, int minimalCount, ElementTryParser<T> parser, out List<T> result) where T : class
{
var lexer = new Lexer (input);
result = new List<T> ();
while (true) {
Token token;
T parsedValue;
if (!parser (lexer, out parsedValue, out token))
return false;
if (parsedValue != null)
result.Add (parsedValue);
if (token == Token.Type.End) {
if (minimalCount > result.Count)
return false;
return true;
}
}
}
}
}

View File

@ -0,0 +1,414 @@
//
// ContentDispositionHeaderValue.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
using System.Text;
using System.Globalization;
namespace System.Net.Http.Headers
{
public class ContentDispositionHeaderValue : ICloneable
{
string dispositionType;
List<NameValueHeaderValue> parameters;
private ContentDispositionHeaderValue ()
{
}
public ContentDispositionHeaderValue (string dispositionType)
{
DispositionType = dispositionType;
}
protected ContentDispositionHeaderValue (ContentDispositionHeaderValue source)
{
if (source == null)
throw new ArgumentNullException ("source");
dispositionType = source.dispositionType;
if (source.parameters != null) {
foreach (var item in source.parameters)
Parameters.Add (new NameValueHeaderValue (item));
}
}
public DateTimeOffset? CreationDate {
get {
return GetDateValue ("creation-date");
}
set {
SetDateValue ("creation-date", value);
}
}
public string DispositionType {
get {
return dispositionType;
}
set {
Parser.Token.Check (value);
dispositionType = value;
}
}
public string FileName {
get {
var value = FindParameter ("filename");
if (value == null)
return null;
return DecodeValue (value, false);
}
set {
if (value != null)
value = EncodeBase64Value (value);
SetValue ("filename", value);
}
}
public string FileNameStar {
get {
var value = FindParameter ("filename*");
if (value == null)
return null;
return DecodeValue (value, true);
}
set {
if (value != null)
value = EncodeRFC5987 (value);
SetValue ("filename*", value);
}
}
public DateTimeOffset? ModificationDate {
get {
return GetDateValue ("modification-date");
}
set {
SetDateValue ("modification-date", value);
}
}
public string Name {
get {
return FindParameter ("name");
}
set {
SetValue ("name", value);
}
}
public ICollection<NameValueHeaderValue> Parameters {
get {
return parameters ?? (parameters = new List<NameValueHeaderValue> ());
}
}
public DateTimeOffset? ReadDate {
get {
return GetDateValue ("read-date");
}
set {
SetDateValue ("read-date", value);
}
}
public long? Size {
get {
var found = FindParameter ("size");
long result;
if (Parser.Long.TryParse (found, out result))
return result;
return null;
}
set {
if (value == null) {
SetValue ("size", null);
return;
}
if (value < 0)
throw new ArgumentOutOfRangeException ("value");
SetValue ("size", value.Value.ToString (CultureInfo.InvariantCulture));
}
}
object ICloneable.Clone ()
{
return new ContentDispositionHeaderValue (this);
}
public override bool Equals (object obj)
{
var source = obj as ContentDispositionHeaderValue;
return source != null &&
string.Equals (source.dispositionType, dispositionType, StringComparison.OrdinalIgnoreCase) &&
source.parameters.SequenceEqual (parameters);
}
string FindParameter (string name)
{
if (parameters == null)
return null;
foreach (var entry in parameters) {
if (string.Equals (entry.Name, name, StringComparison.OrdinalIgnoreCase))
return entry.Value;
}
return null;
}
DateTimeOffset? GetDateValue (string name)
{
var value = FindParameter (name);
if (value == null || value == null)
return null;
if (value.Length < 3)
return null;
if (value[0] == '\"')
value = value.Substring (1, value.Length - 2);
DateTimeOffset offset;
if (Lexer.TryGetDateValue (value, out offset))
return offset;
return null;
}
static string EncodeBase64Value (string value)
{
for (int i = 0; i < value.Length; ++i) {
var ch = value[i];
if (ch > 127) {
var encoding = Encoding.UTF8;
return string.Format ("\"=?{0}?B?{1}?=\"",
encoding.WebName, Convert.ToBase64String (encoding.GetBytes (value)));
}
}
if (!Lexer.IsValidToken (value))
return "\"" + value + "\"";
return value;
}
static string EncodeRFC5987 (string value)
{
var encoding = Encoding.UTF8;
StringBuilder sb = new StringBuilder (value.Length + 11);
sb.Append (encoding.WebName);
sb.Append ('\'');
sb.Append ('\'');
for (int i = 0; i < value.Length; ++i) {
var ch = value[i];
if (ch > 127) {
foreach (var b in encoding.GetBytes (new[] { ch })) {
sb.Append ('%');
sb.Append (b.ToString ("X2"));
}
continue;
}
sb.Append (ch);
}
return sb.ToString ();
}
static string DecodeValue (string value, bool extendedNotation)
{
//
// A short (length <= 78 characters)
// parameter value containing only non-`tspecials' characters SHOULD be
// represented as a single `token'. A short parameter value containing
// only ASCII characters, but including `tspecials' characters, SHOULD
// be represented as `quoted-string'. Parameter values longer than 78
// characters, or which contain non-ASCII characters, MUST be encoded as
// specified in [RFC 2184].
//
if (value.Length < 2)
return value;
string[] sep;
Encoding encoding;
// Quoted string
if (value[0] == '\"') {
//
// Is Base64 encoded ?
// encoded-word := "=?" charset "?" encoding "?" encoded-text "?="
//
sep = value.Split ('?');
if (sep.Length != 5 || sep[0] != "\"=" || sep[4] != "=\"" || (sep[2] != "B" && sep[2] != "b"))
return value;
try {
encoding = Encoding.GetEncoding (sep[1]);
return encoding.GetString (Convert.FromBase64String (sep[3]));
} catch {
return value;
}
}
if (!extendedNotation)
return value;
//
// RFC 5987: Charset/Language Encoding
//
sep = value.Split ('\'');
if (sep.Length != 3)
return null;
try {
encoding = Encoding.GetEncoding (sep[0]);
} catch {
return null;
}
// TODO: What to do with sep[1] language
value = sep[2];
int pct_encoded = value.IndexOf ('%');
if (pct_encoded < 0)
return value;
StringBuilder sb = new StringBuilder ();
byte[] buffer = null;
int buffer_pos = 0;
for (int i = 0; i < value.Length;) {
var ch = value[i];
if (ch == '%') {
var unescaped = ch;
ch = Uri.HexUnescape (value, ref i);
if (ch != unescaped) {
if (buffer == null)
buffer = new byte[value.Length - i + 1];
buffer[buffer_pos++] = (byte) ch;
continue;
}
} else {
++i;
}
if (buffer_pos != 0) {
sb.Append (encoding.GetChars (buffer, 0, buffer_pos));
buffer_pos = 0;
}
sb.Append (ch);
}
if (buffer_pos != 0) {
sb.Append (encoding.GetChars (buffer, 0, buffer_pos));
}
return sb.ToString ();
}
public override int GetHashCode ()
{
return dispositionType.ToLowerInvariant ().GetHashCode () ^
HashCodeCalculator.Calculate (parameters);
}
public static ContentDispositionHeaderValue Parse (string input)
{
ContentDispositionHeaderValue value;
if (TryParse (input, out value))
return value;
throw new FormatException (input);
}
void SetDateValue (string key, DateTimeOffset? value)
{
SetValue (key, value == null ? null : ("\"" + value.Value.ToString ("r", CultureInfo.InvariantCulture)) + "\"");
}
void SetValue (string key, string value)
{
if (parameters == null)
parameters = new List<NameValueHeaderValue> ();
parameters.SetValue (key, value);
}
public override string ToString ()
{
return dispositionType + CollectionExtensions.ToString (parameters);
}
public static bool TryParse (string input, out ContentDispositionHeaderValue parsedValue)
{
parsedValue = null;
var lexer = new Lexer (input);
var t = lexer.Scan ();
if (t.Kind != Token.Type.Token)
return false;
List<NameValueHeaderValue> parameters = null;
var type = lexer.GetStringValue (t);
t = lexer.Scan ();
switch (t.Kind) {
case Token.Type.SeparatorSemicolon:
if (!NameValueHeaderValue.TryParseParameters (lexer, out parameters, out t) || t != Token.Type.End)
return false;
break;
case Token.Type.End:
break;
default:
return false;
}
parsedValue = new ContentDispositionHeaderValue () {
dispositionType = type,
parameters = parameters
};
return true;
}
}
}

View File

@ -0,0 +1,229 @@
//
// ContentRangeHeaderValue.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
using System.Text;
using System.Globalization;
namespace System.Net.Http.Headers
{
public class ContentRangeHeaderValue : ICloneable
{
string unit = "bytes";
private ContentRangeHeaderValue ()
{
}
public ContentRangeHeaderValue (long length)
{
if (length < 0)
throw new ArgumentOutOfRangeException ("length");
this.Length = length;
}
public ContentRangeHeaderValue (long from, long to)
{
if (from < 0 || from > to)
throw new ArgumentOutOfRangeException ("from");
this.From = from;
this.To = to;
}
public ContentRangeHeaderValue (long from, long to, long length)
: this (from, to)
{
if (length < 0)
throw new ArgumentOutOfRangeException ("length");
if (to > length)
throw new ArgumentOutOfRangeException ("to");
this.Length = length;
}
public long? From { get; private set; }
public bool HasLength {
get {
return Length != null;
}
}
public bool HasRange {
get {
return From != null;
}
}
public long? Length { get; private set; }
public long? To { get; private set; }
public string Unit {
get {
return unit;
}
set {
if (value == null)
throw new ArgumentNullException ("Unit");
Parser.Token.Check (value);
unit = value;
}
}
object ICloneable.Clone ()
{
return MemberwiseClone ();
}
public override bool Equals (object obj)
{
var source = obj as ContentRangeHeaderValue;
if (source == null)
return false;
return source.Length == Length && source.From == From && source.To == To &&
string.Equals (source.unit, unit, StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode ()
{
return Unit.GetHashCode () ^ Length.GetHashCode () ^
From.GetHashCode () ^ To.GetHashCode () ^
unit.ToLowerInvariant ().GetHashCode ();
}
public static ContentRangeHeaderValue Parse (string input)
{
ContentRangeHeaderValue value;
if (TryParse (input, out value))
return value;
throw new FormatException (input);
}
public static bool TryParse (string input, out ContentRangeHeaderValue parsedValue)
{
parsedValue = null;
var lexer = new Lexer (input);
var t = lexer.Scan ();
if (t != Token.Type.Token)
return false;
var value = new ContentRangeHeaderValue ();
value.unit = lexer.GetStringValue (t);
t = lexer.Scan ();
if (t != Token.Type.Token)
return false;
int nvalue;
if (!lexer.IsStarStringValue (t)) {
if (!lexer.TryGetNumericValue (t, out nvalue)) {
var s = lexer.GetStringValue (t);
if (s.Length < 3)
return false;
var sep = s.Split ('-');
if (sep.Length != 2)
return false;
if (!int.TryParse (sep[0], NumberStyles.None, CultureInfo.InvariantCulture, out nvalue))
return false;
value.From = nvalue;
if (!int.TryParse (sep[1], NumberStyles.None, CultureInfo.InvariantCulture, out nvalue))
return false;
value.To = nvalue;
} else {
value.From = nvalue;
t = lexer.Scan ();
if (t != Token.Type.SeparatorDash)
return false;
t = lexer.Scan ();
if (!lexer.TryGetNumericValue (t, out nvalue))
return false;
value.To = nvalue;
}
}
t = lexer.Scan ();
if (t != Token.Type.SeparatorSlash)
return false;
t = lexer.Scan ();
if (!lexer.IsStarStringValue (t)) {
if (!lexer.TryGetNumericValue (t, out nvalue))
return false;
value.Length = nvalue;
}
t = lexer.Scan ();
if (t != Token.Type.End)
return false;
parsedValue = value;
return true;
}
public override string ToString ()
{
var sb = new StringBuilder (unit);
sb.Append (" ");
if (From == null) {
sb.Append ("*");
} else {
sb.Append (From.Value.ToString (CultureInfo.InvariantCulture));
sb.Append ("-");
sb.Append (To.Value.ToString (CultureInfo.InvariantCulture));
}
sb.Append ("/");
sb.Append (Length == null ? "*" :
Length.Value.ToString (CultureInfo.InvariantCulture));
return sb.ToString ();
}
}
}

View File

@ -0,0 +1,147 @@
//
// EntityTagHeaderValue.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
public class EntityTagHeaderValue : ICloneable
{
static readonly EntityTagHeaderValue any = new EntityTagHeaderValue () { Tag = "*" };
public EntityTagHeaderValue (string tag)
{
Parser.Token.CheckQuotedString (tag);
Tag = tag;
}
public EntityTagHeaderValue (string tag, bool isWeak)
: this (tag)
{
IsWeak = isWeak;
}
internal EntityTagHeaderValue ()
{
}
public static EntityTagHeaderValue Any {
get {
return any;
}
}
public bool IsWeak { get; internal set; }
public string Tag { get; internal set; }
object ICloneable.Clone ()
{
return MemberwiseClone ();
}
public override bool Equals (object obj)
{
var source = obj as EntityTagHeaderValue;
return source != null && source.Tag == Tag &&
string.Equals (source.Tag, Tag, StringComparison.Ordinal);
}
public override int GetHashCode ()
{
return IsWeak.GetHashCode () ^ Tag.GetHashCode ();
}
public static EntityTagHeaderValue Parse (string input)
{
EntityTagHeaderValue value;
if (TryParse (input, out value))
return value;
throw new FormatException (input);
}
public static bool TryParse (string input, out EntityTagHeaderValue parsedValue)
{
var lexer = new Lexer (input);
Token token;
if (TryParseElement (lexer, out parsedValue, out token) && token == Token.Type.End)
return true;
parsedValue = null;
return false;
}
static bool TryParseElement (Lexer lexer, out EntityTagHeaderValue parsedValue, out Token t)
{
parsedValue = null;
t = lexer.Scan ();
bool is_weak = false;
if (t == Token.Type.Token) {
var s = lexer.GetStringValue (t);
if (s == "*") {
parsedValue = any;
t = lexer.Scan ();
return true;
}
if (s != "W" || lexer.PeekChar () != '/')
return false;
is_weak = true;
lexer.EatChar ();
t = lexer.Scan ();
}
if (t != Token.Type.QuotedString)
return false;
parsedValue = new EntityTagHeaderValue ();
parsedValue.Tag = lexer.GetStringValue (t);
parsedValue.IsWeak = is_weak;
t = lexer.Scan ();
return true;
}
internal static bool TryParse (string input, int minimalCount, out List<EntityTagHeaderValue> result)
{
return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result);
}
public override string ToString ()
{
return IsWeak ?
"W/" + Tag :
Tag;
}
}
}

View File

@ -0,0 +1,50 @@
//
// HashCodeCalculator.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
static class HashCodeCalculator
{
public static int Calculate<T> (ICollection<T> list)
{
if (list == null)
return 0;
int hash = 17;
foreach (var item in list) {
unchecked {
hash = hash * 29 + item.GetHashCode ();
}
}
return hash;
}
}
}

View File

@ -0,0 +1,152 @@
//
// HeaderInfo.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011, 2014 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Diagnostics;
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
delegate bool TryParseDelegate<T> (string value, out T result);
delegate bool TryParseListDelegate<T> (string value, int minimalCount, out List<T> result);
abstract class HeaderInfo
{
class HeaderTypeInfo<T, U> : HeaderInfo where U : class
{
readonly TryParseDelegate<T> parser;
public HeaderTypeInfo (string name, TryParseDelegate<T> parser, HttpHeaderKind headerKind)
: base (name, headerKind)
{
this.parser = parser;
}
public override void AddToCollection (object collection, object value)
{
Debug.Assert (AllowsMany);
var c = (HttpHeaderValueCollection<U>) collection;
var list = value as List<U>;
if (list != null)
c.AddRange (list);
else
c.Add ((U) value);
}
protected override object CreateCollection (HttpHeaders headers, HeaderInfo headerInfo)
{
return new HttpHeaderValueCollection<U> (headers, headerInfo);
}
public override List<string> ToStringCollection (object collection)
{
if (collection == null)
return null;
var c = (HttpHeaderValueCollection<U>) collection;
if (c.Count == 0)
return null;
var list = new List<string> ();
foreach (var item in c) {
list.Add (item.ToString ());
}
return list;
}
public override bool TryParse (string value, out object result)
{
T tresult;
bool b = parser (value, out tresult);
result = tresult;
return b;
}
}
class CollectionHeaderTypeInfo<T, U> : HeaderTypeInfo<T, U> where U : class
{
readonly int minimalCount;
TryParseListDelegate<T> parser;
public CollectionHeaderTypeInfo (string name, TryParseListDelegate<T> parser, HttpHeaderKind headerKind, int minimalCount)
: base (name, null, headerKind)
{
this.parser = parser;
this.minimalCount = minimalCount;
AllowsMany = true;
}
public override bool TryParse (string value, out object result)
{
List<T> tresult;
if (!parser (value, minimalCount, out tresult)) {
result = null;
return false;
}
result = tresult;
return true;
}
}
public bool AllowsMany;
public readonly HttpHeaderKind HeaderKind;
public readonly string Name;
protected HeaderInfo (string name, HttpHeaderKind headerKind)
{
this.Name = name;
this.HeaderKind = headerKind;
}
public static HeaderInfo CreateSingle<T> (string name, TryParseDelegate<T> parser, HttpHeaderKind headerKind)
{
return new HeaderTypeInfo<T, object> (name, parser, headerKind);
}
//
// Headers with #rule for defining lists of elements or *rule for defining occurences of elements
//
public static HeaderInfo CreateMulti<T> (string name, TryParseListDelegate<T> elementParser, HttpHeaderKind headerKind, int minimalCount = 1) where T : class
{
return new CollectionHeaderTypeInfo<T, T> (name, elementParser, headerKind, minimalCount);
}
public object CreateCollection (HttpHeaders headers)
{
return CreateCollection (headers, this);
}
public abstract void AddToCollection (object collection, object value);
protected abstract object CreateCollection (HttpHeaders headers, HeaderInfo headerInfo);
public abstract List<string> ToStringCollection (object collection);
public abstract bool TryParse (string value, out object result);
}
}

View File

@ -0,0 +1,145 @@
//
// HttpContentHeaders.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
public sealed class HttpContentHeaders : HttpHeaders
{
readonly HttpContent content;
internal HttpContentHeaders (HttpContent content)
: base (HttpHeaderKind.Content)
{
this.content = content;
}
public ICollection<string> Allow {
get {
return GetValues<string> ("Allow");
}
}
public ICollection<string> ContentEncoding {
get {
return GetValues<string> ("Content-Encoding");
}
}
public ContentDispositionHeaderValue ContentDisposition {
get {
return GetValue<ContentDispositionHeaderValue> ("Content-Disposition");
}
set {
AddOrRemove ("Content-Disposition", value);
}
}
public ICollection<string> ContentLanguage {
get {
return GetValues<string> ("Content-Language");
}
}
public long? ContentLength {
get {
long? v = GetValue<long?> ("Content-Length");
if (v != null)
return v;
v = content.LoadedBufferLength;
if (v != null)
return v;
long l;
if (content.TryComputeLength (out l))
return l;
return null;
}
set {
AddOrRemove ("Content-Length", value);
}
}
public Uri ContentLocation {
get {
return GetValue<Uri> ("Content-Location");
}
set {
AddOrRemove ("Content-Location", value);
}
}
public byte[] ContentMD5 {
get {
return GetValue<byte[]> ("Content-MD5");
}
set {
AddOrRemove ("Content-MD5", value, Parser.MD5.ToString);
}
}
public ContentRangeHeaderValue ContentRange {
get {
return GetValue<ContentRangeHeaderValue> ("Content-Range");
}
set {
AddOrRemove ("Content-Range", value);
}
}
public MediaTypeHeaderValue ContentType {
get {
return GetValue<MediaTypeHeaderValue> ("Content-Type");
}
set {
AddOrRemove ("Content-Type", value);
}
}
public DateTimeOffset? Expires {
get {
return GetValue<DateTimeOffset?> ("Expires");
}
set {
AddOrRemove ("Expires", value, Parser.DateTime.ToString);
}
}
public DateTimeOffset? LastModified {
get {
return GetValue<DateTimeOffset?> ("Last-Modified");
}
set {
AddOrRemove ("Last-Modified", value, Parser.DateTime.ToString);
}
}
}
}

View File

@ -0,0 +1,39 @@
//
// HttpHeaderKind.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
namespace System.Net.Http.Headers
{
[Flags]
enum HttpHeaderKind
{
None = 0,
Request = 1,
Response = 1 << 1,
Content = 1 << 2
}
}

View File

@ -0,0 +1,130 @@
//
// HttpHeaderValueCollection.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
using System.Collections;
namespace System.Net.Http.Headers
{
public sealed class HttpHeaderValueCollection<T> : ICollection<T> where T : class
{
readonly List<T> list;
readonly HttpHeaders headers;
readonly HeaderInfo headerInfo;
internal HttpHeaderValueCollection (HttpHeaders headers, HeaderInfo headerInfo)
{
list = new List<T> ();
this.headers = headers;
this.headerInfo = headerInfo;
}
public int Count {
get {
return list.Count;
}
}
public bool IsReadOnly {
get {
return false;
}
}
public void Add (T item)
{
list.Add (item);
}
internal void AddRange (List<T> values)
{
list.AddRange (values);
}
public void Clear ()
{
list.Clear ();
}
public bool Contains (T item)
{
return list.Contains (item);
}
public void CopyTo (T[] array, int arrayIndex)
{
list.CopyTo (array, arrayIndex);
}
public void ParseAdd (string input)
{
headers.AddValue (input, headerInfo, false);
}
public bool Remove (T item)
{
return list.Remove (item);
}
public override string ToString ()
{
// This implementation prints different values than
// what .NET does when one of the values is invalid
// But it better represents what is actually hold by
// the collection
return string.Join (", ", list);
}
public bool TryParseAdd (string input)
{
return headers.AddValue (input, headerInfo, true);
}
public IEnumerator<T> GetEnumerator ()
{
return list.GetEnumerator ();
}
IEnumerator IEnumerable.GetEnumerator ()
{
return GetEnumerator ();
}
internal T Find (Predicate<T> predicate)
{
return list.Find (predicate);
}
internal void Remove (Predicate<T> predicate)
{
T item = Find (predicate);
if (item != null)
Remove (item);
}
}
}

View File

@ -0,0 +1,488 @@
//
// HttpHeaders.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace System.Net.Http.Headers
{
public abstract class HttpHeaders : IEnumerable<KeyValuePair<string, IEnumerable<string>>>
{
class HeaderBucket
{
//
// headers can hold an object of 3 kinds
// - simple type for parsed single values (e.g. DateTime)
// - CollectionHeader for multi-value headers
// - List<string> for not checked single values
//
public object Parsed;
List<string> values;
public readonly Func<object, string> CustomToString;
public HeaderBucket (object parsed, Func<object, string> converter = null)
{
this.Parsed = parsed;
this.CustomToString = converter;
}
public bool HasStringValues {
get {
return values != null && values.Count > 0;
}
}
public List<string> Values {
get {
return values ?? (values = new List<string> ());
}
set {
values = value;
}
}
public string ParsedToString ()
{
if (Parsed == null)
return null;
if (CustomToString != null)
return CustomToString (Parsed);
return Parsed.ToString ();
}
}
static readonly Dictionary<string, HeaderInfo> known_headers;
static HttpHeaders ()
{
var headers = new[] {
HeaderInfo.CreateMulti<MediaTypeWithQualityHeaderValue> ("Accept", MediaTypeWithQualityHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateMulti<StringWithQualityHeaderValue> ("Accept-Charset", StringWithQualityHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateMulti<StringWithQualityHeaderValue> ("Accept-Encoding", StringWithQualityHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateMulti<StringWithQualityHeaderValue> ("Accept-Language", StringWithQualityHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateMulti<string> ("Accept-Ranges", CollectionParser.TryParse, HttpHeaderKind.Response),
HeaderInfo.CreateSingle<TimeSpan> ("Age", Parser.TimeSpanSeconds.TryParse, HttpHeaderKind.Response),
HeaderInfo.CreateMulti<string> ("Allow", CollectionParser.TryParse, HttpHeaderKind.Content, 0),
HeaderInfo.CreateSingle<AuthenticationHeaderValue> ("Authorization", AuthenticationHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<CacheControlHeaderValue> ("Cache-Control", CacheControlHeaderValue.TryParse, HttpHeaderKind.Request | HttpHeaderKind.Response),
HeaderInfo.CreateMulti<string> ("Connection", CollectionParser.TryParse, HttpHeaderKind.Request | HttpHeaderKind.Response),
HeaderInfo.CreateMulti<string> ("Content-Encoding", CollectionParser.TryParse, HttpHeaderKind.Content),
HeaderInfo.CreateMulti<string> ("Content-Language", CollectionParser.TryParse, HttpHeaderKind.Content),
HeaderInfo.CreateSingle<long> ("Content-Length", Parser.Long.TryParse, HttpHeaderKind.Content),
HeaderInfo.CreateSingle<Uri> ("Content-Location", Parser.Uri.TryParse, HttpHeaderKind.Content),
HeaderInfo.CreateSingle<byte[]> ("Content-MD5", Parser.MD5.TryParse, HttpHeaderKind.Content),
HeaderInfo.CreateSingle<ContentRangeHeaderValue> ("Content-Range", ContentRangeHeaderValue.TryParse, HttpHeaderKind.Content),
HeaderInfo.CreateSingle<MediaTypeHeaderValue> ("Content-Type", MediaTypeHeaderValue.TryParse, HttpHeaderKind.Content),
HeaderInfo.CreateSingle<DateTimeOffset> ("Date", Parser.DateTime.TryParse, HttpHeaderKind.Request | HttpHeaderKind.Response),
HeaderInfo.CreateSingle<EntityTagHeaderValue> ("ETag", EntityTagHeaderValue.TryParse, HttpHeaderKind.Response),
HeaderInfo.CreateMulti<NameValueWithParametersHeaderValue> ("Expect", NameValueWithParametersHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<DateTimeOffset> ("Expires", Parser.DateTime.TryParse, HttpHeaderKind.Content),
HeaderInfo.CreateSingle<string> ("From", Parser.EmailAddress.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<Uri> ("Host", Parser.Uri.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateMulti<EntityTagHeaderValue> ("If-Match", EntityTagHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<DateTimeOffset> ("If-Modified-Since", Parser.DateTime.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateMulti<EntityTagHeaderValue> ("If-None-Match", EntityTagHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<RangeConditionHeaderValue> ("If-Range", RangeConditionHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<DateTimeOffset> ("If-Unmodified-Since", Parser.DateTime.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<DateTimeOffset> ("Last-Modified", Parser.DateTime.TryParse, HttpHeaderKind.Content),
HeaderInfo.CreateSingle<Uri> ("Location", Parser.Uri.TryParse, HttpHeaderKind.Response),
HeaderInfo.CreateSingle<int> ("Max-Forwards", Parser.Int.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateMulti<NameValueHeaderValue> ("Pragma", NameValueHeaderValue.TryParsePragma, HttpHeaderKind.Request | HttpHeaderKind.Response),
HeaderInfo.CreateMulti<AuthenticationHeaderValue> ("Proxy-Authenticate", AuthenticationHeaderValue.TryParse, HttpHeaderKind.Response),
HeaderInfo.CreateSingle<AuthenticationHeaderValue> ("Proxy-Authorization", AuthenticationHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<RangeHeaderValue> ("Range", RangeHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<Uri> ("Referer", Parser.Uri.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateSingle<RetryConditionHeaderValue> ("Retry-After", RetryConditionHeaderValue.TryParse, HttpHeaderKind.Response),
HeaderInfo.CreateMulti<ProductInfoHeaderValue> ("Server", ProductInfoHeaderValue.TryParse, HttpHeaderKind.Response),
HeaderInfo.CreateMulti<TransferCodingWithQualityHeaderValue> ("TE", TransferCodingWithQualityHeaderValue.TryParse, HttpHeaderKind.Request, 0),
HeaderInfo.CreateMulti<string> ("Trailer", CollectionParser.TryParse, HttpHeaderKind.Request | HttpHeaderKind.Response),
HeaderInfo.CreateMulti<TransferCodingHeaderValue> ("Transfer-Encoding", TransferCodingHeaderValue.TryParse, HttpHeaderKind.Request | HttpHeaderKind.Response),
HeaderInfo.CreateMulti<ProductHeaderValue> ("Upgrade", ProductHeaderValue.TryParse, HttpHeaderKind.Request | HttpHeaderKind.Response),
HeaderInfo.CreateMulti<ProductInfoHeaderValue> ("User-Agent", ProductInfoHeaderValue.TryParse, HttpHeaderKind.Request),
HeaderInfo.CreateMulti<string> ("Vary", CollectionParser.TryParse, HttpHeaderKind.Response),
HeaderInfo.CreateMulti<ViaHeaderValue> ("Via", ViaHeaderValue.TryParse, HttpHeaderKind.Request | HttpHeaderKind.Response),
HeaderInfo.CreateMulti<WarningHeaderValue> ("Warning", WarningHeaderValue.TryParse, HttpHeaderKind.Request | HttpHeaderKind.Response),
HeaderInfo.CreateMulti<AuthenticationHeaderValue> ("WWW-Authenticate", AuthenticationHeaderValue.TryParse, HttpHeaderKind.Response)
};
known_headers = new Dictionary<string, HeaderInfo> (StringComparer.OrdinalIgnoreCase);
foreach (var header in headers) {
known_headers.Add (header.Name, header);
}
}
readonly Dictionary<string, HeaderBucket> headers;
readonly HttpHeaderKind HeaderKind;
internal bool? connectionclose, transferEncodingChunked;
protected HttpHeaders ()
{
headers = new Dictionary<string, HeaderBucket> (StringComparer.OrdinalIgnoreCase);
}
internal HttpHeaders (HttpHeaderKind headerKind)
: this ()
{
this.HeaderKind = headerKind;
}
public void Add (string name, string value)
{
Add (name, new[] { value });
}
public void Add (string name, IEnumerable<string> values)
{
if (values == null)
throw new ArgumentNullException ("values");
AddInternal (name, values, CheckName (name), false);
}
internal bool AddValue (string value, HeaderInfo headerInfo, bool ignoreInvalid)
{
return AddInternal (headerInfo.Name, new [] { value }, headerInfo, ignoreInvalid);
}
bool AddInternal (string name, IEnumerable<string> values, HeaderInfo headerInfo, bool ignoreInvalid)
{
HeaderBucket bucket;
headers.TryGetValue (name, out bucket);
bool ok = true;
foreach (var value in values) {
bool first_entry = bucket == null;
if (headerInfo != null) {
object parsed_value;
if (!headerInfo.TryParse (value, out parsed_value)) {
if (ignoreInvalid) {
ok = false;
continue;
}
throw new FormatException ();
}
if (headerInfo.AllowsMany) {
if (bucket == null)
bucket = new HeaderBucket (headerInfo.CreateCollection (this));
headerInfo.AddToCollection (bucket.Parsed, parsed_value);
} else {
if (bucket != null)
throw new FormatException ();
bucket = new HeaderBucket (parsed_value);
}
} else {
if (bucket == null)
bucket = new HeaderBucket (null);
bucket.Values.Add (value ?? string.Empty);
}
if (first_entry) {
headers.Add (name, bucket);
}
}
return ok;
}
public bool TryAddWithoutValidation (string name, string value)
{
return TryAddWithoutValidation (name, new[] { value });
}
public bool TryAddWithoutValidation (string name, IEnumerable<string> values)
{
if (values == null)
throw new ArgumentNullException ("values");
HeaderInfo headerInfo;
if (!TryCheckName (name, out headerInfo))
return false;
AddInternal (name, values, null, true);
return true;
}
HeaderInfo CheckName (string name)
{
if (string.IsNullOrEmpty (name))
throw new ArgumentException ("name");
Parser.Token.Check (name);
HeaderInfo headerInfo;
if (known_headers.TryGetValue (name, out headerInfo) && (headerInfo.HeaderKind & HeaderKind) == 0) {
if (HeaderKind != HttpHeaderKind.None && ((HeaderKind | headerInfo.HeaderKind) & HttpHeaderKind.Content) != 0)
throw new InvalidOperationException (name);
return null;
}
return headerInfo;
}
bool TryCheckName (string name, out HeaderInfo headerInfo)
{
if (!Parser.Token.TryCheck (name)) {
headerInfo = null;
return false;
}
if (known_headers.TryGetValue (name, out headerInfo) && (headerInfo.HeaderKind & HeaderKind) == 0) {
if (HeaderKind != HttpHeaderKind.None && ((HeaderKind | headerInfo.HeaderKind) & HttpHeaderKind.Content) != 0)
return false;
}
return true;
}
public void Clear ()
{
connectionclose = null;
transferEncodingChunked = null;
headers.Clear ();
}
public bool Contains (string name)
{
CheckName (name);
return headers.ContainsKey (name);
}
public IEnumerator<KeyValuePair<string, IEnumerable<string>>> GetEnumerator ()
{
foreach (var entry in headers) {
var bucket = headers[entry.Key];
HeaderInfo headerInfo;
known_headers.TryGetValue (entry.Key, out headerInfo);
var svalues = GetAllHeaderValues (bucket, headerInfo);
if (svalues == null)
continue;
yield return new KeyValuePair<string, IEnumerable<string>> (entry.Key, svalues);
}
}
IEnumerator IEnumerable.GetEnumerator ()
{
return GetEnumerator ();
}
public IEnumerable<string> GetValues (string name)
{
CheckName (name);
IEnumerable<string> values;
if (!TryGetValues (name, out values))
throw new InvalidOperationException ();
return values;
}
public bool Remove (string name)
{
CheckName (name);
return headers.Remove (name);
}
public bool TryGetValues (string name, out IEnumerable<string> values)
{
HeaderInfo headerInfo;
if (!TryCheckName (name, out headerInfo)) {
values = null;
return false;
}
HeaderBucket bucket;
if (!headers.TryGetValue (name, out bucket)) {
values = null;
return false;
}
values = GetAllHeaderValues (bucket, headerInfo);
return true;
}
public override string ToString ()
{
var sb = new StringBuilder ();
foreach (var entry in this) {
sb.Append (entry.Key);
sb.Append (": ");
bool first = true;
foreach (var v in entry.Value) {
if (!first)
sb.Append (", ");
sb.Append (v);
first = false;
}
sb.Append ("\r\n");
}
return sb.ToString ();
}
internal void AddOrRemove (string name, string value)
{
if (string.IsNullOrEmpty (value))
Remove (name);
else
SetValue (name, value);
}
internal void AddOrRemove<T> (string name, T value, Func<object, string> converter = null) where T : class
{
if (value == null)
Remove (name);
else
SetValue (name, value, converter);
}
internal void AddOrRemove<T> (string name, T? value) where T : struct
{
AddOrRemove<T> (name, value, null);
}
internal void AddOrRemove<T> (string name, T? value, Func<object, string> converter) where T : struct
{
if (!value.HasValue)
Remove (name);
else
SetValue (name, value, converter);
}
List<string> GetAllHeaderValues (HeaderBucket bucket, HeaderInfo headerInfo)
{
List<string> string_values = null;
if (headerInfo != null && headerInfo.AllowsMany) {
string_values = headerInfo.ToStringCollection (bucket.Parsed);
} else {
if (bucket.Parsed != null) {
string s = bucket.ParsedToString ();
if (!string.IsNullOrEmpty (s)) {
string_values = new List<string> ();
string_values.Add (s);
}
}
}
if (bucket.HasStringValues) {
if (string_values == null)
string_values = new List<string> ();
string_values.AddRange (bucket.Values);
}
return string_values;
}
internal static HttpHeaderKind GetKnownHeaderKind (string name)
{
if (string.IsNullOrEmpty (name))
throw new ArgumentException ("name");
HeaderInfo headerInfo;
if (known_headers.TryGetValue (name, out headerInfo))
return headerInfo.HeaderKind;
return HttpHeaderKind.None;
}
internal T GetValue<T> (string name)
{
HeaderBucket value;
if (!headers.TryGetValue (name, out value))
return default (T);
if (value.HasStringValues) {
var hinfo = known_headers[name];
object pvalue;
if (!hinfo.TryParse (value.Values [0], out pvalue)) {
return typeof (T) == typeof (string) ? (T) (object) value.Values[0] : default (T);
}
value.Parsed = pvalue;
value.Values = null;
}
return (T) value.Parsed;
}
internal HttpHeaderValueCollection<T> GetValues<T> (string name) where T : class
{
HeaderBucket value;
if (!headers.TryGetValue (name, out value)) {
value = new HeaderBucket (new HttpHeaderValueCollection<T> (this, known_headers [name]));
headers.Add (name, value);
}
if (value.HasStringValues) {
var hinfo = known_headers[name];
if (value.Parsed == null)
value.Parsed = hinfo.CreateCollection (this);
object pvalue;
for (int i = 0; i < value.Values.Count; ++i) {
if (!hinfo.TryParse (value.Values[i], out pvalue))
continue;
hinfo.AddToCollection (value.Parsed, pvalue);
value.Values.RemoveAt (i);
--i;
}
}
return (HttpHeaderValueCollection<T>) value.Parsed;
}
void SetValue<T> (string name, T value, Func<object, string> toStringConverter = null)
{
headers[name] = new HeaderBucket (value, toStringConverter);
}
}
}

View File

@ -0,0 +1,322 @@
//
// HttpRequestHeaders.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
public sealed class HttpRequestHeaders : HttpHeaders
{
bool? expectContinue;
internal HttpRequestHeaders ()
: base (HttpHeaderKind.Request)
{
}
public HttpHeaderValueCollection<MediaTypeWithQualityHeaderValue> Accept {
get {
return GetValues<MediaTypeWithQualityHeaderValue> ("Accept");
}
}
public HttpHeaderValueCollection<StringWithQualityHeaderValue> AcceptCharset {
get {
return GetValues<StringWithQualityHeaderValue> ("Accept-Charset");
}
}
public HttpHeaderValueCollection<StringWithQualityHeaderValue> AcceptEncoding {
get {
return GetValues<StringWithQualityHeaderValue> ("Accept-Encoding");
}
}
public HttpHeaderValueCollection<StringWithQualityHeaderValue> AcceptLanguage {
get {
return GetValues<StringWithQualityHeaderValue> ("Accept-Language");
}
}
public AuthenticationHeaderValue Authorization {
get {
return GetValue<AuthenticationHeaderValue> ("Authorization");
}
set {
AddOrRemove ("Authorization", value);
}
}
public CacheControlHeaderValue CacheControl {
get {
return GetValue<CacheControlHeaderValue> ("Cache-Control");
}
set {
AddOrRemove ("Cache-Control", value);
}
}
public HttpHeaderValueCollection<string> Connection {
get {
return GetValues<string> ("Connection");
}
}
public bool? ConnectionClose {
get {
if (connectionclose == true || Connection.Find (l => string.Equals (l, "close", StringComparison.OrdinalIgnoreCase)) != null)
return true;
return connectionclose;
}
set {
if (connectionclose == value)
return;
Connection.Remove ("close");
if (value == true)
Connection.Add ("close");
connectionclose = value;
}
}
internal bool ConnectionKeepAlive {
get {
return Connection.Find (l => string.Equals (l, "Keep-Alive", StringComparison.OrdinalIgnoreCase)) != null;
}
}
public DateTimeOffset? Date {
get {
return GetValue<DateTimeOffset?> ("Date");
}
set {
AddOrRemove ("Date", value, Parser.DateTime.ToString);
}
}
public HttpHeaderValueCollection<NameValueWithParametersHeaderValue> Expect {
get {
return GetValues<NameValueWithParametersHeaderValue> ("Expect");
}
}
public bool? ExpectContinue {
get {
if (expectContinue.HasValue)
return expectContinue;
var found = TransferEncoding.Find (l => string.Equals (l.Value, "100-continue", StringComparison.OrdinalIgnoreCase));
return found != null ? true : (bool?) null;
}
set {
if (expectContinue == value)
return;
Expect.Remove (l => l.Name == "100-continue");
if (value == true)
Expect.Add (new NameValueWithParametersHeaderValue ("100-continue"));
expectContinue = value;
}
}
public string From {
get {
return GetValue<string> ("From");
}
set {
if (!string.IsNullOrEmpty (value) && !Parser.EmailAddress.TryParse (value, out value))
throw new FormatException ();
AddOrRemove ("From", value);
}
}
public string Host {
get {
return GetValue<string> ("Host");
}
set {
AddOrRemove ("Host", value);
}
}
public HttpHeaderValueCollection<EntityTagHeaderValue> IfMatch {
get {
return GetValues<EntityTagHeaderValue> ("If-Match");
}
}
public DateTimeOffset? IfModifiedSince {
get {
return GetValue<DateTimeOffset?> ("If-Modified-Since");
}
set {
AddOrRemove ("If-Modified-Since", value, Parser.DateTime.ToString);
}
}
public HttpHeaderValueCollection<EntityTagHeaderValue> IfNoneMatch {
get {
return GetValues<EntityTagHeaderValue> ("If-None-Match");
}
}
public RangeConditionHeaderValue IfRange {
get {
return GetValue<RangeConditionHeaderValue> ("If-Range");
}
set {
AddOrRemove ("If-Range", value);
}
}
public DateTimeOffset? IfUnmodifiedSince {
get {
return GetValue<DateTimeOffset?> ("If-Unmodified-Since");
}
set {
AddOrRemove ("If-Unmodified-Since", value, Parser.DateTime.ToString);
}
}
public int? MaxForwards {
get {
return GetValue<int?> ("Max-Forwards");
}
set {
AddOrRemove ("Max-Forwards", value);
}
}
public HttpHeaderValueCollection<NameValueHeaderValue> Pragma {
get {
return GetValues<NameValueHeaderValue> ("Pragma");
}
}
public AuthenticationHeaderValue ProxyAuthorization {
get {
return GetValue<AuthenticationHeaderValue> ("Proxy-Authorization");
}
set {
AddOrRemove ("Proxy-Authorization", value);
}
}
public RangeHeaderValue Range {
get {
return GetValue<RangeHeaderValue> ("Range");
}
set {
AddOrRemove ("Range", value);
}
}
public Uri Referrer {
get {
return GetValue<Uri> ("Referer");
}
set {
AddOrRemove ("Referer", value);
}
}
public HttpHeaderValueCollection<TransferCodingWithQualityHeaderValue> TE {
get {
return GetValues<TransferCodingWithQualityHeaderValue> ("TE");
}
}
public HttpHeaderValueCollection<string> Trailer {
get {
return GetValues<string> ("Trailer");
}
}
public HttpHeaderValueCollection<TransferCodingHeaderValue> TransferEncoding {
get {
return GetValues<TransferCodingHeaderValue> ("Transfer-Encoding");
}
}
public bool? TransferEncodingChunked {
get {
if (transferEncodingChunked.HasValue)
return transferEncodingChunked;
var found = TransferEncoding.Find (l => string.Equals (l.Value, "chunked", StringComparison.OrdinalIgnoreCase));
return found != null ? true : (bool?) null;
}
set {
if (value == transferEncodingChunked)
return;
TransferEncoding.Remove (l => l.Value == "chunked");
if (value == true)
TransferEncoding.Add (new TransferCodingHeaderValue ("chunked"));
transferEncodingChunked = value;
}
}
public HttpHeaderValueCollection<ProductHeaderValue> Upgrade {
get {
return GetValues<ProductHeaderValue> ("Upgrade");
}
}
public HttpHeaderValueCollection<ProductInfoHeaderValue> UserAgent {
get {
return GetValues<ProductInfoHeaderValue> ("User-Agent");
}
}
public HttpHeaderValueCollection<ViaHeaderValue> Via {
get {
return GetValues<ViaHeaderValue> ("Via");
}
}
public HttpHeaderValueCollection<WarningHeaderValue> Warning {
get {
return GetValues<WarningHeaderValue> ("Warning");
}
}
internal void AddHeaders (HttpRequestHeaders headers)
{
foreach (var header in headers) {
TryAddWithoutValidation (header.Key, header.Value);
}
}
}
}

View File

@ -0,0 +1,205 @@
//
// HttpResponseHeaders.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
public sealed class HttpResponseHeaders : HttpHeaders
{
internal HttpResponseHeaders ()
: base (HttpHeaderKind.Response)
{
}
public HttpHeaderValueCollection<string> AcceptRanges {
get {
return GetValues<string> ("Accept-Ranges");
}
}
public TimeSpan? Age {
get {
return GetValue<TimeSpan?> ("Age");
}
set {
AddOrRemove ("Age", value, l => ((long)((TimeSpan)l).TotalSeconds).ToString ());
}
}
public CacheControlHeaderValue CacheControl {
get {
return GetValue<CacheControlHeaderValue> ("Cache-Control");
}
set {
AddOrRemove ("Cache-Control", value);
}
}
public HttpHeaderValueCollection<string> Connection {
get {
return GetValues<string> ("Connection");
}
}
public bool? ConnectionClose {
get {
if (connectionclose == true || Connection.Find (l => string.Equals (l, "close", StringComparison.OrdinalIgnoreCase)) != null)
return true;
return connectionclose;
}
set {
if (connectionclose == value)
return;
Connection.Remove ("close");
if (value == true)
Connection.Add ("close");
connectionclose = value;
}
}
public DateTimeOffset? Date {
get {
return GetValue<DateTimeOffset?> ("Date");
}
set {
AddOrRemove ("Date", value, Parser.DateTime.ToString);
}
}
public EntityTagHeaderValue ETag {
get {
return GetValue<EntityTagHeaderValue> ("ETag");
}
set {
AddOrRemove ("ETag", value);
}
}
public Uri Location {
get {
return GetValue<Uri> ("Location");
}
set {
AddOrRemove ("Location", value);
}
}
public HttpHeaderValueCollection<NameValueHeaderValue> Pragma {
get {
return GetValues<NameValueHeaderValue> ("Pragma");
}
}
public HttpHeaderValueCollection<AuthenticationHeaderValue> ProxyAuthenticate {
get {
return GetValues<AuthenticationHeaderValue> ("Proxy-Authenticate");
}
}
public RetryConditionHeaderValue RetryAfter {
get {
return GetValue<RetryConditionHeaderValue> ("Retry-After");
}
set {
AddOrRemove ("Retry-After", value);
}
}
public HttpHeaderValueCollection<ProductInfoHeaderValue> Server {
get {
return GetValues<ProductInfoHeaderValue> ("Server");
}
}
public HttpHeaderValueCollection<string> Trailer {
get {
return GetValues<string> ("Trailer");
}
}
public HttpHeaderValueCollection<TransferCodingHeaderValue> TransferEncoding {
get {
return GetValues<TransferCodingHeaderValue> ("Transfer-Encoding");
}
}
public bool? TransferEncodingChunked {
get {
if (transferEncodingChunked.HasValue)
return transferEncodingChunked;
var found = TransferEncoding.Find (l => StringComparer.OrdinalIgnoreCase.Equals (l.Value, "chunked"));
return found != null ? true : (bool?) null;
}
set {
if (value == transferEncodingChunked)
return;
TransferEncoding.Remove (l => l.Value == "chunked");
if (value == true)
TransferEncoding.Add (new TransferCodingHeaderValue ("chunked"));
transferEncodingChunked = value;
}
}
public HttpHeaderValueCollection<ProductHeaderValue> Upgrade {
get {
return GetValues<ProductHeaderValue> ("Upgrade");
}
}
public HttpHeaderValueCollection<string> Vary {
get {
return GetValues<string> ("Vary");
}
}
public HttpHeaderValueCollection<ViaHeaderValue> Via {
get {
return GetValues<ViaHeaderValue> ("Via");
}
}
public HttpHeaderValueCollection<WarningHeaderValue> Warning {
get {
return GetValues<WarningHeaderValue> ("Warning");
}
}
public HttpHeaderValueCollection<AuthenticationHeaderValue> WwwAuthenticate {
get {
return GetValues<AuthenticationHeaderValue> ("WWW-Authenticate");
}
}
}
}

View File

@ -0,0 +1,332 @@
//
// Lexer.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Globalization;
namespace System.Net.Http.Headers
{
struct Token
{
public enum Type
{
Error,
End,
Token,
QuotedString,
SeparatorEqual,
SeparatorSemicolon,
SeparatorSlash,
SeparatorDash,
SeparatorComma,
OpenParens,
}
public static readonly Token Empty = new Token (Type.Token, 0, 0);
readonly Type type;
public Token (Type type, int startPosition, int endPosition)
: this ()
{
this.type = type;
StartPosition = startPosition;
EndPosition = endPosition;
}
public int StartPosition { get; private set; }
public int EndPosition { get; private set; }
public Type Kind {
get {
return type;
}
}
public static implicit operator Token.Type (Token token)
{
return token.type;
}
public override string ToString ()
{
return type.ToString ();
}
}
class Lexer
{
// any CHAR except CTLs or separators
static readonly bool[] token_chars = {
/*0*/ false, false, false, false, false, false, false, false, false, false,
/*10*/ false, false, false, false, false, false, false, false, false, false,
/*20*/ false, false, false, false, false, false, false, false, false, false,
/*30*/ false, false, false, true, false, true, true, true, true, true,
/*40*/ false, false, true, true, false, true, true, false, true, true,
/*50*/ true, true, true, true, true, true, true, true, false, false,
/*60*/ false, false, false, false, false, true, true, true, true, true,
/*70*/ true, true, true, true, true, true, true, true, true, true,
/*80*/ true, true, true, true, true, true, true, true, true, true,
/*90*/ true, false, false, false, true, true, true, true, true, true,
/*100*/ true, true, true, true, true, true, true, true, true, true,
/*110*/ true, true, true, true, true, true, true, true, true, true,
/*120*/ true, true, true, false, true, false
};
static readonly int last_token_char = token_chars.Length;
static readonly string[] dt_formats = new[] {
"r",
"dddd, dd'-'MMM'-'yy HH:mm:ss 'GMT'",
"ddd MMM d HH:mm:ss yyyy",
"d MMM yy H:m:s",
"ddd, d MMM yyyy H:m:s zzz"
};
readonly string s;
int pos;
public Lexer (string stream)
{
this.s = stream;
}
public int Position {
get {
return pos;
}
set {
pos = value;
}
}
public string GetStringValue (Token token)
{
return s.Substring (token.StartPosition, token.EndPosition - token.StartPosition);
}
public string GetStringValue (Token start, Token end)
{
return s.Substring (start.StartPosition, end.EndPosition - start.StartPosition);
}
public string GetQuotedStringValue (Token start)
{
return s.Substring (start.StartPosition + 1, start.EndPosition - start.StartPosition - 2);
}
public string GetRemainingStringValue (int position)
{
return position > s.Length ? null : s.Substring (position);
}
public bool IsStarStringValue (Token token)
{
return (token.EndPosition - token.StartPosition) == 1 && s[token.StartPosition] == '*';
}
public bool TryGetNumericValue (Token token, out int value)
{
return int.TryParse (GetStringValue (token), NumberStyles.None, CultureInfo.InvariantCulture, out value);
}
public TimeSpan? TryGetTimeSpanValue (Token token)
{
int seconds;
if (TryGetNumericValue (token, out seconds)) {
return TimeSpan.FromSeconds (seconds);
}
return null;
}
public bool TryGetDateValue (Token token, out DateTimeOffset value)
{
string text = token == Token.Type.QuotedString ?
s.Substring (token.StartPosition + 1, token.EndPosition - token.StartPosition - 2) :
GetStringValue (token);
return TryGetDateValue (text, out value);
}
public static bool TryGetDateValue (string text, out DateTimeOffset value)
{
const DateTimeStyles DefaultStyles = DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowWhiteSpaces;
return DateTimeOffset.TryParseExact (text, dt_formats, DateTimeFormatInfo.InvariantInfo, DefaultStyles, out value);
}
public bool TryGetDoubleValue (Token token, out double value)
{
string s = GetStringValue (token);
return double.TryParse (s, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out value);
}
public static bool IsValidToken (string input)
{
int i = 0;
//
// any CHAR except CTLs or separator
//
for (; i < input.Length; ++i) {
char s = input[i];
if (s > last_token_char || !token_chars[s])
return false;
}
return i > 0;
}
public void EatChar ()
{
++pos;
}
public int PeekChar ()
{
return pos < s.Length ? s[pos] : -1;
}
public bool ScanCommentOptional (out string value)
{
Token t;
if (ScanCommentOptional (out value, out t))
return true;
return t == Token.Type.End;
}
public bool ScanCommentOptional (out string value, out Token readToken)
{
readToken = Scan ();
if (readToken != Token.Type.OpenParens) {
value = null;
return false;
}
while (pos < s.Length) {
var ch = s[pos];
if (ch == ')') {
++pos;
var start = readToken.StartPosition;
value = s.Substring (start, pos - start);
return true;
}
// any OCTET except CTLs, but including LWS
if (ch < 32 || ch > 126)
break;
++pos;
}
value = null;
return false;
}
public Token Scan ()
{
int start = pos;
if (s == null)
return new Token (Token.Type.Error, 0, 0);
Token.Type ttype;
if (pos >= s.Length) {
ttype = Token.Type.End;
} else {
ttype = Token.Type.Error;
start:
char ch = s[pos++];
switch (ch) {
case ' ':
case '\t':
if (pos == s.Length) {
ttype = Token.Type.End;
break;
}
goto start;
case '=':
ttype = Token.Type.SeparatorEqual;
break;
case ';':
ttype = Token.Type.SeparatorSemicolon;
break;
case '/':
ttype = Token.Type.SeparatorSlash;
break;
case '-':
ttype = Token.Type.SeparatorDash;
break;
case ',':
ttype = Token.Type.SeparatorComma;
break;
case '"':
// Quoted string
start = pos - 1;
while (pos < s.Length) {
ch = s[pos];
if (ch == '"') {
++pos;
ttype = Token.Type.QuotedString;
break;
}
// any OCTET except CTLs, but including LWS
if (ch < 32 || ch > 126)
break;
++pos;
}
break;
case '(':
start = pos - 1;
ttype = Token.Type.OpenParens;
break;
default:
if (ch <= last_token_char && token_chars[ch]) {
start = pos - 1;
ttype = Token.Type.Token;
while (pos < s.Length) {
ch = s[pos];
if (ch > last_token_char || !token_chars[ch]) {
break;
}
++pos;
}
}
break;
}
}
return new Token (ttype, start, pos);
}
}
}

View File

@ -0,0 +1,191 @@
//
// MediaTypeHeaderValue.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
public class MediaTypeHeaderValue : ICloneable
{
internal List<NameValueHeaderValue> parameters;
internal string media_type;
public MediaTypeHeaderValue (string mediaType)
{
MediaType = mediaType;
}
protected MediaTypeHeaderValue (MediaTypeHeaderValue source)
{
if (source == null)
throw new ArgumentNullException ("source");
media_type = source.media_type;
if (source.parameters != null) {
foreach (var item in source.parameters)
Parameters.Add (new NameValueHeaderValue (item));
}
}
internal MediaTypeHeaderValue ()
{
}
public string CharSet {
get {
if (parameters == null)
return null;
var found = parameters.Find (l => string.Equals (l.Name, "charset", StringComparison.OrdinalIgnoreCase));
if (found == null)
return null;
return found.Value;
}
set {
if (parameters == null)
parameters = new List<NameValueHeaderValue> ();
parameters.SetValue ("charset", value);
}
}
public string MediaType {
get {
return media_type;
}
set {
if (value == null)
throw new ArgumentNullException ("MediaType");
string temp;
var token = TryParseMediaType (new Lexer (value), out temp);
if (token == null || token.Value.Kind != Token.Type.End)
throw new FormatException ();
media_type = temp;
}
}
public ICollection<NameValueHeaderValue> Parameters {
get {
return parameters ?? (parameters = new List<NameValueHeaderValue> ());
}
}
object ICloneable.Clone ()
{
return new MediaTypeHeaderValue (this);
}
public override bool Equals (object obj)
{
var source = obj as MediaTypeHeaderValue;
if (source == null)
return false;
return string.Equals (source.media_type, media_type, StringComparison.OrdinalIgnoreCase) &&
source.parameters.SequenceEqual (parameters);
}
public override int GetHashCode ()
{
return media_type.ToLowerInvariant ().GetHashCode () ^ HashCodeCalculator.Calculate (parameters);
}
public static MediaTypeHeaderValue Parse (string input)
{
MediaTypeHeaderValue value;
if (TryParse (input, out value))
return value;
throw new FormatException (input);
}
public override string ToString ()
{
if (parameters == null)
return media_type;
return media_type + CollectionExtensions.ToString (parameters);
}
public static bool TryParse (string input, out MediaTypeHeaderValue parsedValue)
{
parsedValue = null;
var lexer = new Lexer (input);
string media;
List<NameValueHeaderValue> parameters = null;
var token = TryParseMediaType (lexer, out media);
if (token == null)
return false;
switch (token.Value.Kind) {
case Token.Type.SeparatorSemicolon:
Token t;
if (!NameValueHeaderValue.TryParseParameters (lexer, out parameters, out t) || t != Token.Type.End)
return false;
break;
case Token.Type.End:
break;
default:
return false;
}
parsedValue = new MediaTypeHeaderValue () {
media_type = media,
parameters = parameters
};
return true;
}
internal static Token? TryParseMediaType (Lexer lexer, out string media)
{
media = null;
var token = lexer.Scan ();
if (token != Token.Type.Token)
return null;
if (lexer.Scan () != Token.Type.SeparatorSlash)
return null;
var token2 = lexer.Scan ();
if (token2 != Token.Type.Token)
return null;
media = lexer.GetStringValue (token) + "/" + lexer.GetStringValue (token2);
return lexer.Scan ();
}
}
}

View File

@ -0,0 +1,106 @@
//
// MediaTypeWithQualityHeaderValue.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
public sealed class MediaTypeWithQualityHeaderValue : MediaTypeHeaderValue
{
public MediaTypeWithQualityHeaderValue (string mediaType)
: base (mediaType)
{
}
public MediaTypeWithQualityHeaderValue (string mediaType, double quality)
: this (mediaType)
{
Quality = quality;
}
private MediaTypeWithQualityHeaderValue ()
{
}
public double? Quality {
get {
return QualityValue.GetValue (parameters);
}
set {
QualityValue.SetValue (ref parameters, value);
}
}
public new static MediaTypeWithQualityHeaderValue Parse (string input)
{
MediaTypeWithQualityHeaderValue value;
if (TryParse (input, out value))
return value;
throw new FormatException ();
}
public static bool TryParse (string input, out MediaTypeWithQualityHeaderValue parsedValue)
{
var lexer = new Lexer (input);
Token token;
if (TryParseElement (lexer, out parsedValue, out token) && token == Token.Type.End)
return true;
parsedValue = null;
return false;
}
static bool TryParseElement (Lexer lexer, out MediaTypeWithQualityHeaderValue parsedValue, out Token t)
{
parsedValue = null;
string media;
List<NameValueHeaderValue> parameters = null;
var token = TryParseMediaType (lexer, out media);
if (token == null) {
t = Token.Empty;
return false;
}
t = token.Value;
if (t == Token.Type.SeparatorSemicolon && (!NameValueHeaderValue.TryParseParameters (lexer, out parameters, out t) || t != Token.Type.End))
return false;
parsedValue = new MediaTypeWithQualityHeaderValue ();
parsedValue.media_type = media;
parsedValue.parameters = parameters;
return true;
}
internal static bool TryParse (string input, int minimalCount, out List<MediaTypeWithQualityHeaderValue> result)
{
return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result);
}
}
}

View File

@ -0,0 +1,213 @@
//
// NameValueHeaderValue.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
public class NameValueHeaderValue : ICloneable
{
internal string value;
public NameValueHeaderValue (string name)
: this (name, null)
{
}
public NameValueHeaderValue (string name, string value)
{
Parser.Token.Check (name);
this.Name = name;
this.Value = value;
}
protected internal NameValueHeaderValue (NameValueHeaderValue source)
{
this.Name = source.Name;
this.value = source.value;
}
internal NameValueHeaderValue ()
{
}
public string Name { get; internal set; }
public string Value {
get {
return value;
}
set {
if (!string.IsNullOrEmpty (value)) {
var lexer = new Lexer (value);
var token = lexer.Scan ();
if (lexer.Scan () != Token.Type.End || !(token == Token.Type.Token || token == Token.Type.QuotedString))
throw new FormatException ();
value = lexer.GetStringValue (token);
}
this.value = value;
}
}
internal static NameValueHeaderValue Create (string name, string value)
{
return new NameValueHeaderValue () {
Name = name,
value = value
};
}
object ICloneable.Clone ()
{
return new NameValueHeaderValue (this);
}
public override int GetHashCode ()
{
int hc = Name.ToLowerInvariant ().GetHashCode ();
if (!string.IsNullOrEmpty (value)) {
hc ^= value.ToLowerInvariant ().GetHashCode ();
}
return hc;
}
public override bool Equals (object obj)
{
var source = obj as NameValueHeaderValue;
if (source == null || !string.Equals (source.Name, Name, StringComparison.OrdinalIgnoreCase))
return false;
if (string.IsNullOrEmpty (value))
return string.IsNullOrEmpty (source.value);
return string.Equals (source.value, value, StringComparison.OrdinalIgnoreCase);
}
public static NameValueHeaderValue Parse (string input)
{
NameValueHeaderValue value;
if (TryParse (input, out value))
return value;
throw new FormatException (input);
}
internal static bool TryParsePragma (string input, int minimalCount, out List<NameValueHeaderValue> result)
{
return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result);
}
internal static bool TryParseParameters (Lexer lexer, out List<NameValueHeaderValue> result, out Token t)
{
var list = new List<NameValueHeaderValue> ();
result = null;
while (true) {
var attr = lexer.Scan ();
if (attr != Token.Type.Token) {
t = Token.Empty;
return false;
}
string value = null;
t = lexer.Scan ();
if (t == Token.Type.SeparatorEqual) {
t = lexer.Scan ();
if (t != Token.Type.Token && t != Token.Type.QuotedString)
return false;
value = lexer.GetStringValue (t);
t = lexer.Scan ();
}
list.Add (new NameValueHeaderValue () {
Name = lexer.GetStringValue (attr),
value = value
});
if (t == Token.Type.SeparatorSemicolon)
continue;
result = list;
return true;
}
}
public override string ToString ()
{
if (string.IsNullOrEmpty (value))
return Name;
return Name + "=" + value;
}
public static bool TryParse (string input, out NameValueHeaderValue parsedValue)
{
var lexer = new Lexer (input);
Token token;
if (TryParseElement (lexer, out parsedValue, out token) && token == Token.Type.End)
return true;
parsedValue = null;
return false;
}
static bool TryParseElement (Lexer lexer, out NameValueHeaderValue parsedValue, out Token t)
{
parsedValue = null;
t = lexer.Scan ();
if (t != Token.Type.Token)
return false;
parsedValue = new NameValueHeaderValue () {
Name = lexer.GetStringValue (t),
};
t = lexer.Scan ();
if (t == Token.Type.SeparatorEqual) {
t = lexer.Scan ();
if (t == Token.Type.Token || t == Token.Type.QuotedString) {
parsedValue.value = lexer.GetStringValue (t);
t = lexer.Scan ();
} else {
return false;
}
}
return true;
}
}
}

View File

@ -0,0 +1,154 @@
//
// NameValueWithParametersHeaderValue.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections.Generic;
namespace System.Net.Http.Headers
{
public class NameValueWithParametersHeaderValue : NameValueHeaderValue, ICloneable
{
List<NameValueHeaderValue> parameters;
public NameValueWithParametersHeaderValue (string name)
: base (name)
{
}
public NameValueWithParametersHeaderValue (string name, string value)
: base (name, value)
{
}
protected NameValueWithParametersHeaderValue (NameValueWithParametersHeaderValue source)
: base (source)
{
if (source.parameters != null) {
foreach (var item in source.parameters)
Parameters.Add (item);
}
}
private NameValueWithParametersHeaderValue ()
: base ()
{
}
public ICollection<NameValueHeaderValue> Parameters {
get {
return parameters ?? (parameters = new List<NameValueHeaderValue> ());
}
}
object ICloneable.Clone ()
{
return new NameValueWithParametersHeaderValue (this);
}
public override bool Equals (object obj)
{
var source = obj as NameValueWithParametersHeaderValue;
if (source == null)
return false;
return base.Equals (obj) && source.parameters.SequenceEqual (parameters);
}
public override int GetHashCode ()
{
return base.GetHashCode () ^ HashCodeCalculator.Calculate (parameters);
}
public static new NameValueWithParametersHeaderValue Parse (string input)
{
NameValueWithParametersHeaderValue value;
if (TryParse (input, out value))
return value;
throw new FormatException (input);
}
public override string ToString ()
{
if (parameters == null || parameters.Count == 0)
return base.ToString ();
return base.ToString () + CollectionExtensions.ToString (parameters);
}
public static bool TryParse (string input, out NameValueWithParametersHeaderValue parsedValue)
{
var lexer = new Lexer (input);
Token token;
if (TryParseElement (lexer, out parsedValue, out token) && token == Token.Type.End)
return true;
parsedValue = null;
return false;
}
internal static bool TryParse (string input, int minimalCount, out List<NameValueWithParametersHeaderValue> result)
{
return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result);
}
static bool TryParseElement (Lexer lexer, out NameValueWithParametersHeaderValue parsedValue, out Token t)
{
parsedValue = null;
t = lexer.Scan ();
if (t != Token.Type.Token)
return false;
parsedValue = new NameValueWithParametersHeaderValue () {
Name = lexer.GetStringValue (t),
};
t = lexer.Scan ();
if (t == Token.Type.SeparatorEqual) {
t = lexer.Scan ();
if (t == Token.Type.Token || t == Token.Type.QuotedString) {
parsedValue.value = lexer.GetStringValue (t);
t = lexer.Scan ();
} else {
return false;
}
}
if (t == Token.Type.SeparatorSemicolon) {
List<NameValueHeaderValue> result;
if (!TryParseParameters (lexer, out result, out t))
return false;
parsedValue.parameters = result;
}
return true;
}
}
}

Some files were not shown because too many files have changed in this diff Show More