diff options
author | Marek Safar <marek.safar@gmail.com> | 2014-01-13 17:46:58 +0400 |
---|---|---|
committer | Marek Safar <marek.safar@gmail.com> | 2014-01-13 17:48:12 +0400 |
commit | 2d0272d9c2e8a99741ea9520ab02d05d84bc4981 (patch) | |
tree | 2753b5929fec45c89550503270ae622c1e30a2c0 /mcs/class | |
parent | 0b1316bccb9bfe45d3329c4ad30fa07cfd66e42c (diff) |
[System.Net.Http] Add parsing of multi-value strings. Fixes #17132
Diffstat (limited to 'mcs/class')
24 files changed, 598 insertions, 162 deletions
diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/AuthenticationHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/AuthenticationHeaderValue.cs index 33f87db8f03..7bb2496a94d 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/AuthenticationHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/AuthenticationHeaderValue.cs @@ -26,6 +26,8 @@ // 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 @@ -85,8 +87,23 @@ namespace System.Net.Http.Headers public static bool TryParse (string input, out AuthenticationHeaderValue parsedValue) { var lexer = new Lexer (input); - var t = lexer.Scan (); - if (t != Token.Type.Token || !(lexer.PeekChar () == ' ' || lexer.PeekChar () == -1)) { + 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; } @@ -95,8 +112,11 @@ namespace System.Net.Http.Headers parsedValue.Scheme = lexer.GetStringValue (t); t = lexer.Scan (); - if (t != Token.Type.End) + 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; } diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/CollectionParser.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/CollectionParser.cs new file mode 100644 index 00000000000..cbaafe0f808 --- /dev/null +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/CollectionParser.cs @@ -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; + } + } + } + } +}
\ No newline at end of file diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/ContentDispositionHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/ContentDispositionHeaderValue.cs index a4d1ae275b4..9dd41b3d330 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/ContentDispositionHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/ContentDispositionHeaderValue.cs @@ -394,7 +394,7 @@ namespace System.Net.Http.Headers switch (t.Kind) { case Token.Type.SeparatorSemicolon: - if (!NameValueHeaderValue.TryParseParameters (lexer, out parameters)) + if (!NameValueHeaderValue.TryParseParameters (lexer, out parameters, out t) || t != Token.Type.End) return false; break; case Token.Type.End: diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/EntityTagHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/EntityTagHeaderValue.cs index 9b10983bbfc..8c255cfaede 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/EntityTagHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/EntityTagHeaderValue.cs @@ -26,6 +26,8 @@ // 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 @@ -85,14 +87,32 @@ namespace System.Net.Http.Headers 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; + } - var lexer = new Lexer (input); - var t = lexer.Scan (); + 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) { - if (lexer.GetStringValue (t) != "W" || lexer.PeekChar () != '/') + var s = lexer.GetStringValue (t); + if (s == "*") { + parsedValue = any; + + t = lexer.Scan (); + return true; + } + + if (s != "W" || lexer.PeekChar () != '/') return false; is_weak = true; @@ -103,15 +123,20 @@ namespace System.Net.Http.Headers if (t != Token.Type.QuotedString) return false; - if (lexer.Scan () != Token.Type.End) - 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 ? diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/HeaderInfo.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/HeaderInfo.cs index 3772180e52b..6337319ea1e 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/HeaderInfo.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/HeaderInfo.cs @@ -4,7 +4,7 @@ // Authors: // Marek Safar <marek.safar@gmail.com> // -// Copyright (C) 2011 Xamarin Inc (http://www.xamarin.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 @@ -32,6 +32,7 @@ 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 { @@ -89,6 +90,32 @@ namespace System.Net.Http.Headers } } + 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; @@ -104,18 +131,12 @@ namespace System.Net.Http.Headers return new HeaderTypeInfo<T, object> (name, parser, headerKind); } - public static HeaderInfo CreateMulti<T> (string name, TryParseDelegate<T> parser, HttpHeaderKind headerKind) where T : class - { - return new HeaderTypeInfo<T, T> (name, parser, headerKind) { - AllowsMany = true, - }; - } - - public static HeaderInfo CreateMultiList<T> (string name, TryParseDelegate<List<T>> parser, HttpHeaderKind headerKind) where T : class + // + // 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 HeaderTypeInfo<List<T>, T> (name, parser, headerKind) { - AllowsMany = true, - }; + return new CollectionHeaderTypeInfo<T, T> (name, elementParser, headerKind, minimalCount); } public object CreateCollection (HttpHeaders headers) diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/HttpHeaders.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/HttpHeaders.cs index a36652ce4eb..ffef647b5ee 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/HttpHeaders.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/HttpHeaders.cs @@ -90,14 +90,14 @@ namespace System.Net.Http.Headers 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", Parser.Token.TryParse, HttpHeaderKind.Response), + HeaderInfo.CreateMulti<string> ("Accept-Ranges", CollectionParser.TryParse, HttpHeaderKind.Response), HeaderInfo.CreateSingle<TimeSpan> ("Age", Parser.TimeSpanSeconds.TryParse, HttpHeaderKind.Response), - HeaderInfo.CreateMulti<string> ("Allow", Parser.Token.TryParse, HttpHeaderKind.Content), + 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", Parser.Token.TryParse, HttpHeaderKind.Request | HttpHeaderKind.Response), - HeaderInfo.CreateMulti<string> ("Content-Encoding", Parser.Token.TryParse, HttpHeaderKind.Content), - HeaderInfo.CreateMulti<string> ("Content-Language", Parser.Token.TryParse, HttpHeaderKind.Content), + 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), @@ -117,19 +117,19 @@ namespace System.Net.Http.Headers 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.CreateMultiList<NameValueHeaderValue> ("Pragma", NameValueHeaderValue.TryParsePragma, HttpHeaderKind.Request | HttpHeaderKind.Response), + 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), - HeaderInfo.CreateMulti<string> ("Trailer", Parser.Token.TryParse, HttpHeaderKind.Request | 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.CreateMultiList<ProductInfoHeaderValue> ("User-Agent", ProductInfoHeaderValue.TryParse, HttpHeaderKind.Request), - HeaderInfo.CreateMulti<string> ("Vary", Parser.Token.TryParse, 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) diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/Lexer.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/Lexer.cs index 0f98be658d1..f87d4f741bf 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/Lexer.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/Lexer.cs @@ -46,6 +46,8 @@ namespace System.Net.Http.Headers OpenParens, } + public static readonly Token Empty = new Token (Type.Token, 0, 0); + readonly Type type; public Token (Type type, int startPosition, int endPosition) diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/MediaTypeHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/MediaTypeHeaderValue.cs index 5379a6099f1..296010aa2ad 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/MediaTypeHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/MediaTypeHeaderValue.cs @@ -33,7 +33,7 @@ namespace System.Net.Http.Headers public class MediaTypeHeaderValue : ICloneable { internal List<NameValueHeaderValue> parameters; - string media_type; + internal string media_type; public MediaTypeHeaderValue (string mediaType) { @@ -150,7 +150,8 @@ namespace System.Net.Http.Headers switch (token.Value.Kind) { case Token.Type.SeparatorSemicolon: - if (!NameValueHeaderValue.TryParseParameters (lexer, out parameters)) + Token t; + if (!NameValueHeaderValue.TryParseParameters (lexer, out parameters, out t) || t != Token.Type.End) return false; break; case Token.Type.End: @@ -167,37 +168,7 @@ namespace System.Net.Http.Headers return true; } - internal static bool TryParse<T> (string input, out T parsedValue, Func<T> factory) where T : MediaTypeHeaderValue - { - 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: - if (!NameValueHeaderValue.TryParseParameters (lexer, out parameters)) - return false; - break; - case Token.Type.End: - break; - default: - return false; - } - - parsedValue = factory (); - parsedValue.media_type = media; - parsedValue.parameters = parameters; - - return true; - } - - static Token? TryParseMediaType (Lexer lexer, out string media) + internal static Token? TryParseMediaType (Lexer lexer, out string media) { media = null; diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/MediaTypeWithQualityHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/MediaTypeWithQualityHeaderValue.cs index a3415fc1e1a..1cf406dbfdc 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/MediaTypeWithQualityHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/MediaTypeWithQualityHeaderValue.cs @@ -26,6 +26,8 @@ // 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 @@ -65,7 +67,40 @@ namespace System.Net.Http.Headers public static bool TryParse (string input, out MediaTypeWithQualityHeaderValue parsedValue) { - return TryParse (input, out parsedValue, () => new MediaTypeWithQualityHeaderValue ()); + 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); } } } diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/NameValueHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/NameValueHeaderValue.cs index 906ab3aa657..e3766a4eb15 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/NameValueHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/NameValueHeaderValue.cs @@ -32,7 +32,7 @@ namespace System.Net.Http.Headers { public class NameValueHeaderValue : ICloneable { - string value; + internal string value; public NameValueHeaderValue (string name) : this (name, null) @@ -53,11 +53,11 @@ namespace System.Net.Http.Headers this.value = source.value; } - private NameValueHeaderValue () + internal NameValueHeaderValue () { } - public string Name { get; private set; } + public string Name { get; internal set; } public string Value { get { @@ -121,27 +121,22 @@ namespace System.Net.Http.Headers throw new FormatException (input); } - internal static bool TryParseParameters (Lexer lexer, out List<NameValueHeaderValue> result) + internal static bool TryParsePragma (string input, int minimalCount, out List<NameValueHeaderValue> result) { - return TryParseCollection (lexer, out result, Token.Type.SeparatorSemicolon); + return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result); } - internal static bool TryParsePragma (string input, out List<NameValueHeaderValue> result) - { - return TryParseCollection (new Lexer (input), out result, Token.Type.SeparatorComma); - } - - static bool TryParseCollection (Lexer lexer, out List<NameValueHeaderValue> result, Token.Type separator) + internal static bool TryParseParameters (Lexer lexer, out List<NameValueHeaderValue> result, out Token t) { var list = new List<NameValueHeaderValue> (); result = null; - Token t; - - do { + while (true) { var attr = lexer.Scan (); - if (attr != Token.Type.Token) + if (attr != Token.Type.Token) { + t = Token.Empty; return false; + } string value = null; @@ -156,19 +151,17 @@ namespace System.Net.Http.Headers t = lexer.Scan (); } - if (t == separator|| t == Token.Type.End) { - list.Add (new NameValueHeaderValue () { - Name = lexer.GetStringValue (attr), - value = value - }); - } else { - return false; - } + list.Add (new NameValueHeaderValue () { + Name = lexer.GetStringValue (attr), + value = value + }); - } while (t == separator); + if (t == Token.Type.SeparatorSemicolon) + continue; - result = list; - return true; + result = list; + return true; + } } public override string ToString () @@ -181,35 +174,39 @@ namespace System.Net.Http.Headers public static bool TryParse (string input, out NameValueHeaderValue parsedValue) { - parsedValue = null; - var lexer = new Lexer (input); - var t = lexer.Scan (); - if (t != Token.Type.Token && t != Token.Type.QuotedString) - return false; + Token token; + if (TryParseElement (lexer, out parsedValue, out token) && token == Token.Type.End) + return true; - string v = null; - var token2 = lexer.Scan (); - if (token2 != Token.Type.End) { - if (token2 != Token.Type.SeparatorEqual) - return false; - - token2 = lexer.Scan (); + parsedValue = null; + return false; + } - if (token2 == Token.Type.Token || token2 == Token.Type.QuotedString) { - v = lexer.GetStringValue (token2); - token2 = lexer.Scan (); - } + static bool TryParseElement (Lexer lexer, out NameValueHeaderValue parsedValue, out Token t) + { + parsedValue = null; - if (token2 != Token.Type.End) - return false; - } + t = lexer.Scan (); + if (t != Token.Type.Token) + return false; parsedValue = new NameValueHeaderValue () { Name = lexer.GetStringValue (t), - value = v }; + 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; } } diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/NameValueWithParametersHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/NameValueWithParametersHeaderValue.cs index 81d7bb089c1..3d328341845 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/NameValueWithParametersHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/NameValueWithParametersHeaderValue.cs @@ -53,8 +53,8 @@ namespace System.Net.Http.Headers } } - private NameValueWithParametersHeaderValue (NameValueHeaderValue source) - : base (source) + private NameValueWithParametersHeaderValue () + : base () { } @@ -102,15 +102,52 @@ namespace System.Net.Http.Headers public static bool TryParse (string input, out NameValueWithParametersHeaderValue parsedValue) { - List<NameValueHeaderValue> values; - if (!TryParseParameters (new Lexer (input), out values)) { - parsedValue = null; + 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; } - parsedValue = new NameValueWithParametersHeaderValue (values[0]); - values.RemoveAt (0); - parsedValue.parameters = values; return true; } } diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/Parser.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/Parser.cs index a6d80d7375b..79a3ca380c8 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/Parser.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/Parser.cs @@ -28,6 +28,7 @@ using System.Net.Mail; using System.Globalization; +using System.Collections.Generic; namespace System.Net.Http.Headers { diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/ProductHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/ProductHeaderValue.cs index 306c9f228c8..9614df12939 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/ProductHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/ProductHeaderValue.cs @@ -26,6 +26,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +using System.Collections.Generic; + namespace System.Net.Http.Headers { public class ProductHeaderValue : ICloneable @@ -87,15 +89,30 @@ namespace System.Net.Http.Headers public static bool TryParse (string input, out ProductHeaderValue 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; + } - var lexer = new Lexer (input); - var t = lexer.Scan (); + internal static bool TryParse (string input, int minimalCount, out List<ProductHeaderValue> result) + { + return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result); + } + + static bool TryParseElement (Lexer lexer, out ProductHeaderValue parsedValue, out Token t) + { + parsedValue = null; + + t = lexer.Scan (); if (t != Token.Type.Token) return false; - var value = new ProductHeaderValue (); - value.Name = lexer.GetStringValue (t); + parsedValue = new ProductHeaderValue (); + parsedValue.Name = lexer.GetStringValue (t); t = lexer.Scan (); if (t == Token.Type.SeparatorSlash) { @@ -103,14 +120,10 @@ namespace System.Net.Http.Headers if (t != Token.Type.Token) return false; - value.Version = lexer.GetStringValue (t); + parsedValue.Version = lexer.GetStringValue (t); t = lexer.Scan (); } - if (t != Token.Type.End) - return false; - - parsedValue = value; return true; } diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/ProductInfoHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/ProductInfoHeaderValue.cs index daf47ac31e8..baace53ed8e 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/ProductInfoHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/ProductInfoHeaderValue.cs @@ -106,7 +106,7 @@ namespace System.Net.Http.Headers return true; } - internal static bool TryParse (string input, out List<ProductInfoHeaderValue> result) + internal static bool TryParse (string input, int minimalCount, out List<ProductInfoHeaderValue> result) { var list = new List<ProductInfoHeaderValue> (); var lexer = new Lexer (input); @@ -118,8 +118,12 @@ namespace System.Net.Http.Headers return false; if (element == null) { - result = list; - return true; + if (list != null && minimalCount <= list.Count) { + result = list; + return true; + } + + return false; } list.Add (element); diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/StringWithQualityHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/StringWithQualityHeaderValue.cs index 635ed00223c..648d4222e36 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/StringWithQualityHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/StringWithQualityHeaderValue.cs @@ -27,6 +27,8 @@ // using System.Globalization; +using System.Collections.Generic; + namespace System.Net.Http.Headers { public class StringWithQualityHeaderValue : ICloneable @@ -82,10 +84,24 @@ namespace System.Net.Http.Headers public static bool TryParse (string input, out StringWithQualityHeaderValue 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; + } - var lexer = new Lexer (input); - var t = lexer.Scan (); + internal static bool TryParse (string input, int minimalCount, out List<StringWithQualityHeaderValue> result) + { + return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result); + } + + static bool TryParseElement (Lexer lexer, out StringWithQualityHeaderValue parsedValue, out Token t) + { + parsedValue = null; + t = lexer.Scan (); if (t != Token.Type.Token) return false; @@ -120,9 +136,6 @@ namespace System.Net.Http.Headers t = lexer.Scan (); } - if (t != Token.Type.End) - return false; - parsedValue = value; return true; } diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/TransferCodingHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/TransferCodingHeaderValue.cs index 8f0db592831..6e9825ea4ce 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/TransferCodingHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/TransferCodingHeaderValue.cs @@ -32,7 +32,7 @@ namespace System.Net.Http.Headers { public class TransferCodingHeaderValue : ICloneable { - string value; + internal string value; internal List<NameValueHeaderValue> parameters; public TransferCodingHeaderValue (string value) @@ -106,30 +106,36 @@ namespace System.Net.Http.Headers public static bool TryParse (string input, out TransferCodingHeaderValue parsedValue) { - return TryParse (input, out parsedValue, () => new TransferCodingHeaderValue ()); + 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<T> (string input, out T parsedValue, Func<T> factory) where T : TransferCodingHeaderValue + internal static bool TryParse (string input, int minimalCount, out List<TransferCodingHeaderValue> result) + { + return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result); + } + + static bool TryParseElement (Lexer lexer, out TransferCodingHeaderValue parsedValue, out Token t) { parsedValue = null; - var lexer = new Lexer (input); - var t = lexer.Scan (); + t = lexer.Scan (); if (t != Token.Type.Token) return false; - var result = factory (); + var result = new TransferCodingHeaderValue (); result.value = lexer.GetStringValue (t); t = lexer.Scan (); // Parameters parsing - if (t == Token.Type.SeparatorSemicolon) { - if (!NameValueHeaderValue.TryParseParameters (lexer, out result.parameters)) - return false; - } else if (t != Token.Type.End) { + if (t == Token.Type.SeparatorSemicolon && (!NameValueHeaderValue.TryParseParameters (lexer, out result.parameters, out t) || t != Token.Type.End)) return false; - } parsedValue = result; return true; diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/TransferCodingWithQualityHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/TransferCodingWithQualityHeaderValue.cs index 0a6e08373c6..845b95ef5cb 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/TransferCodingWithQualityHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/TransferCodingWithQualityHeaderValue.cs @@ -26,6 +26,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +using System.Collections.Generic; + namespace System.Net.Http.Headers { public sealed class TransferCodingWithQualityHeaderValue : TransferCodingHeaderValue @@ -65,7 +67,39 @@ namespace System.Net.Http.Headers public static bool TryParse (string input, out TransferCodingWithQualityHeaderValue parsedValue) { - return TryParse (input, out parsedValue, () => new TransferCodingWithQualityHeaderValue ()); + 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<TransferCodingWithQualityHeaderValue> result) + { + return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result); + } + + static bool TryParseElement (Lexer lexer, out TransferCodingWithQualityHeaderValue parsedValue, out Token t) + { + parsedValue = null; + + t = lexer.Scan (); + if (t != Token.Type.Token) + return false; + + var result = new TransferCodingWithQualityHeaderValue (); + result.value = lexer.GetStringValue (t); + + t = lexer.Scan (); + + // Parameters parsing + if (t == Token.Type.SeparatorSemicolon && (!NameValueHeaderValue.TryParseParameters (lexer, out result.parameters, out t) || t != Token.Type.End)) + return false; + + parsedValue = result; + return true; } } } diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/ViaHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/ViaHeaderValue.cs index 4da14004e13..9381977ee17 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/ViaHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/ViaHeaderValue.cs @@ -26,6 +26,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +using System.Collections.Generic; + namespace System.Net.Http.Headers { public class ViaHeaderValue : ICloneable @@ -110,11 +112,25 @@ namespace System.Net.Http.Headers public static bool TryParse (string input, out ViaHeaderValue 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; + } - var lexer = new Lexer (input); + internal static bool TryParse (string input, int minimalCount, out List<ViaHeaderValue> result) + { + return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result); + } - var t = lexer.Scan (); + static bool TryParseElement (Lexer lexer, out ViaHeaderValue parsedValue, out Token t) + { + parsedValue = null; + + t = lexer.Scan (); if (t != Token.Type.Token) return false; @@ -150,8 +166,9 @@ namespace System.Net.Http.Headers value.ReceivedBy = lexer.GetStringValue (next, t); string comment; - if (!lexer.ScanCommentOptional (out comment)) - return false; + if (lexer.ScanCommentOptional (out comment, out t)) { + t = lexer.Scan (); + } value.Comment = comment; parsedValue = value; diff --git a/mcs/class/System.Net.Http/System.Net.Http.Headers/WarningHeaderValue.cs b/mcs/class/System.Net.Http/System.Net.Http.Headers/WarningHeaderValue.cs index fc58299cf0b..5c65339ef7a 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.Headers/WarningHeaderValue.cs +++ b/mcs/class/System.Net.Http/System.Net.Http.Headers/WarningHeaderValue.cs @@ -27,6 +27,7 @@ // using System.Globalization; +using System.Collections.Generic; namespace System.Net.Http.Headers { @@ -100,13 +101,28 @@ namespace System.Net.Http.Headers throw new FormatException (input); } - + public static bool TryParse (string input, out WarningHeaderValue 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; + } - var lexer = new Lexer (input); - var t = lexer.Scan (); + internal static bool TryParse (string input, int minimalCount, out List<WarningHeaderValue> result) + { + return CollectionParser.TryParse (input, minimalCount, TryParseElement, out result); + } + + static bool TryParseElement (Lexer lexer, out WarningHeaderValue parsedValue, out Token t) + { + parsedValue = null; + + t = lexer.Scan (); if (t != Token.Type.Token) return false; @@ -148,9 +164,6 @@ namespace System.Net.Http.Headers t = lexer.Scan (); } - if (t != Token.Type.End) - return false; - parsedValue = value; return true; } diff --git a/mcs/class/System.Net.Http/System.Net.Http.dll.sources b/mcs/class/System.Net.Http/System.Net.Http.dll.sources index ccca104fd9d..b40b2a170c1 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.dll.sources +++ b/mcs/class/System.Net.Http/System.Net.Http.dll.sources @@ -22,6 +22,7 @@ System.Net.Http/StringContent.cs System.Net.Http.Headers/AuthenticationHeaderValue.cs System.Net.Http.Headers/CacheControlHeaderValue.cs System.Net.Http.Headers/CollectionExtensions.cs +System.Net.Http.Headers/CollectionParser.cs System.Net.Http.Headers/ContentDispositionHeaderValue.cs System.Net.Http.Headers/ContentRangeHeaderValue.cs System.Net.Http.Headers/EntityTagHeaderValue.cs diff --git a/mcs/class/System.Net.Http/Test/System.Net.Http.Headers/NameValueHeaderValueTest.cs b/mcs/class/System.Net.Http/Test/System.Net.Http.Headers/NameValueHeaderValueTest.cs index 7d08832868a..32cf785a0e9 100644 --- a/mcs/class/System.Net.Http/Test/System.Net.Http.Headers/NameValueHeaderValueTest.cs +++ b/mcs/class/System.Net.Http/Test/System.Net.Http.Headers/NameValueHeaderValueTest.cs @@ -157,6 +157,9 @@ namespace MonoTests.System.Net.Http.Headers NameValueHeaderValue res; Assert.IsFalse (NameValueHeaderValue.TryParse ("", out res), "#1"); Assert.IsNull (res, "#2"); + + Assert.IsFalse (NameValueHeaderValue.TryParse ("\"a\"=b", out res), "#3"); + Assert.IsNull (res, "#4"); } } } diff --git a/mcs/class/System.Net.Http/Test/System.Net.Http/HttpRequestMessageTest.cs b/mcs/class/System.Net.Http/Test/System.Net.Http/HttpRequestMessageTest.cs index b852fdc05fd..79ae114f65b 100644 --- a/mcs/class/System.Net.Http/Test/System.Net.Http/HttpRequestMessageTest.cs +++ b/mcs/class/System.Net.Http/Test/System.Net.Http/HttpRequestMessageTest.cs @@ -35,6 +35,7 @@ using System.Net.Http; using System.Net; using System.Net.Http.Headers; using System.Linq; +using System.IO; namespace MonoTests.System.Net.Http { @@ -361,15 +362,55 @@ namespace MonoTests.System.Net.Http } [Test] - public void Headers_Complex () + public void Headers_MultiValues () { HttpRequestMessage message = new HttpRequestMessage (); HttpRequestHeaders headers = message.Headers; + headers.Add ("Accept", "application/vnd.citrix.requesttokenresponse+xml, application/vnd.citrix.requesttokenchoices+xml"); + headers.Add ("Accept-Charset", "aa ;Q=0,bb;Q=1"); + headers.Add ("Expect", "x=1; v, y=5"); + headers.Add ("If-Match", "\"a\",*, \"b\",*"); headers.Add ("user-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36"); + Assert.AreEqual (2, headers.Accept.Count, "#1a"); + Assert.IsTrue (headers.Accept.SequenceEqual ( + new[] { + new MediaTypeWithQualityHeaderValue ("application/vnd.citrix.requesttokenresponse+xml"), + new MediaTypeWithQualityHeaderValue ("application/vnd.citrix.requesttokenchoices+xml"), + } + ), "#1b"); - Assert.AreEqual (6, headers.UserAgent.Count); + Assert.AreEqual (2, headers.AcceptCharset.Count, "#2a"); + Assert.IsTrue (headers.AcceptCharset.SequenceEqual ( + new[] { + new StringWithQualityHeaderValue ("aa", 0), + new StringWithQualityHeaderValue ("bb", 1), + } + ), "#2b"); + + Assert.AreEqual (2, headers.Expect.Count, "#3a"); + var expect_expected = new[] { + new NameValueWithParametersHeaderValue ("x", "1") { + }, + new NameValueWithParametersHeaderValue ("y", "5"), + }; + expect_expected [0].Parameters.Add (new NameValueHeaderValue ("v")); + Assert.IsTrue (headers.Expect.SequenceEqual ( + expect_expected + ), "#3b"); + + Assert.AreEqual (4, headers.IfMatch.Count, "#4a"); + Assert.IsTrue (headers.IfMatch.SequenceEqual ( + new[] { + new EntityTagHeaderValue ("\"a\""), + EntityTagHeaderValue.Any, + new EntityTagHeaderValue ("\"b\""), + EntityTagHeaderValue.Any + } + ), "#4b"); + + Assert.AreEqual (6, headers.UserAgent.Count, "#10a"); Assert.IsTrue (headers.UserAgent.SequenceEqual ( new[] { @@ -380,7 +421,7 @@ namespace MonoTests.System.Net.Http new ProductInfoHeaderValue ("Chrome", "29.0.1547.62"), new ProductInfoHeaderValue ("Safari", "537.36") } - )); + ), "#10b"); } [Test] diff --git a/mcs/class/System.Net.Http/Test/System.Net.Http/HttpResponseMessageTest.cs b/mcs/class/System.Net.Http/Test/System.Net.Http/HttpResponseMessageTest.cs index b92b3e644d6..b13e8cae417 100644 --- a/mcs/class/System.Net.Http/Test/System.Net.Http/HttpResponseMessageTest.cs +++ b/mcs/class/System.Net.Http/Test/System.Net.Http/HttpResponseMessageTest.cs @@ -295,6 +295,56 @@ namespace MonoTests.System.Net.Http } [Test] + public void Headers_MultiValues () + { + var message = new HttpResponseMessage (); + var headers = message.Headers; + + headers.Add ("Proxy-Authenticate", "x, y, z,i"); + headers.Add ("Upgrade", "HTTP/2.0, SHTTP/1.3, IRC, RTA/x11"); + headers.Add ("Via", "1.0 fred, 1.1 nowhere.com (Apache/1.1)"); + headers.Add ("Warning", "199 Miscellaneous \"w\", 200 a \"b\""); + + Assert.AreEqual (4, headers.ProxyAuthenticate.Count, "#1a"); + Assert.IsTrue (headers.ProxyAuthenticate.SequenceEqual ( + new[] { + new AuthenticationHeaderValue ("x"), + + new AuthenticationHeaderValue ("y"), + new AuthenticationHeaderValue ("z"), + new AuthenticationHeaderValue ("i") + } + ), "#1b"); + + + Assert.AreEqual (4, headers.Upgrade.Count, "#2a"); + Assert.IsTrue (headers.Upgrade.SequenceEqual ( + new[] { + new ProductHeaderValue ("HTTP", "2.0"), + new ProductHeaderValue ("SHTTP", "1.3"), + new ProductHeaderValue ("IRC"), + new ProductHeaderValue ("RTA", "x11") + } + ), "#2b"); + + Assert.AreEqual (2, headers.Via.Count, "#3a"); + Assert.IsTrue (headers.Via.SequenceEqual ( + new[] { + new ViaHeaderValue ("1.0", "fred"), + new ViaHeaderValue ("1.1", "nowhere.com", null, "(Apache/1.1)") + } + ), "#2b"); + + Assert.AreEqual (2, headers.Warning.Count, "#4a"); + Assert.IsTrue (headers.Warning.SequenceEqual ( + new[] { + new WarningHeaderValue (199, "Miscellaneous", "\"w\""), + new WarningHeaderValue (200, "a", "\"b\"") + } + ), "#4b"); + } + + [Test] public void Header_BaseImplementation () { HttpResponseMessage message = new HttpResponseMessage (); diff --git a/mcs/class/System.Net.Http/Test/System.Net.Http/StreamContentTest.cs b/mcs/class/System.Net.Http/Test/System.Net.Http/StreamContentTest.cs index edd4b839c16..413e459526e 100644 --- a/mcs/class/System.Net.Http/Test/System.Net.Http/StreamContentTest.cs +++ b/mcs/class/System.Net.Http/Test/System.Net.Http/StreamContentTest.cs @@ -309,6 +309,21 @@ namespace MonoTests.System.Net.Http } [Test] + public void Headers_Multi () + { + var sc = new StreamContent (MemoryStream.Null); + var headers = sc.Headers; + + headers.Add ("Allow", ""); + headers.Add ("Allow", "a , b, c"); + + Assert.AreEqual (3, headers.Allow.Count, "#1a"); + Assert.IsTrue (headers.Allow.SequenceEqual ( + new[] { "a", "b", "c" } + ), "#1b"); + } + + [Test] public void LoadIntoBuffer () { var ms = new MemoryStream (); |