Implemented more System.Net.Http.Headers
authorMarek Safar <marek.safar@gmail.com>
Wed, 18 Jan 2012 17:40:45 +0000 (17:40 +0000)
committerMarek Safar <marek.safar@gmail.com>
Wed, 18 Jan 2012 18:07:50 +0000 (18:07 +0000)
21 files changed:
mcs/class/System.Net.Http/System.Net.Http.Headers/AuthenticationHeaderValue.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/CacheControlHeaderValue.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/CollectionExtensions.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/Lexer.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/NameValueHeaderValue.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/Parser.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/ProductHeaderValue.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/ProductInfoHeaderValue.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/RangeHeaderValue.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/RangeItemHeaderValue.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/ViaHeaderValue.cs
mcs/class/System.Net.Http/System.Net.Http.Headers/WarningHeaderValue.cs
mcs/class/System.Net.Http/Test/System.Net.Http.Headers/AuthenticationHeaderValueTest.cs
mcs/class/System.Net.Http/Test/System.Net.Http.Headers/CacheControlHeaderValueTest.cs
mcs/class/System.Net.Http/Test/System.Net.Http.Headers/ProductHeaderValueTest.cs
mcs/class/System.Net.Http/Test/System.Net.Http.Headers/ProductInfoHeaderValueTest.cs
mcs/class/System.Net.Http/Test/System.Net.Http.Headers/RangeHeaderValueTest.cs
mcs/class/System.Net.Http/Test/System.Net.Http.Headers/RangeItemHeaderValueTest.cs
mcs/class/System.Net.Http/Test/System.Net.Http.Headers/StringWithQualityHeaderValueTest.cs
mcs/class/System.Net.Http/Test/System.Net.Http.Headers/ViaHeaderValueTest.cs
mcs/class/System.Net.Http/Test/System.Net.Http.Headers/WarningHeaderValueTest.cs

index 26595a07dbfeaec028086598d23917e743385fe0..33f87db8f032bea12e6103e809248456c3557133 100644 (file)
@@ -37,15 +37,16 @@ namespace System.Net.Http.Headers
 
                public AuthenticationHeaderValue (string scheme, string parameter)
                {
-                       if (scheme == null)
-                               throw new ArgumentNullException ("scheme");
-
                        Parser.Token.Check (scheme);
 
                        this.Scheme = scheme;
                        this.Parameter = parameter;
                }
 
+               private AuthenticationHeaderValue ()
+               {
+               }
+
                public string Parameter { get; private set; }
                public string Scheme { get; private set; }
 
@@ -83,7 +84,28 @@ namespace System.Net.Http.Headers
 
                public static bool TryParse (string input, out AuthenticationHeaderValue parsedValue)
                {
-                       throw new NotImplementedException ();
+                       var lexer = new Lexer (input);
+                       var t = lexer.Scan ();
+                       if (t != Token.Type.Token || !(lexer.PeekChar () == ' ' || lexer.PeekChar () == -1)) {
+                               parsedValue = null;
+                               return false;
+                       }
+
+                       parsedValue = new AuthenticationHeaderValue ();
+                       parsedValue.Scheme = lexer.GetStringValue (t);
+
+                       t = lexer.Scan ();
+                       if (t != Token.Type.End)
+                               parsedValue.Parameter = lexer.GetRemainingStringValue (t.StartPosition);
+
+                       return true;
+               }
+
+               public override string ToString ()
+               {
+                       return Parameter != null ?
+                               Scheme + " " + Parameter :
+                               Scheme;
                }
        }
 }
index 9809703c8f5915074bd4ad2cb49914b93f2a1330..2682f1c2815ab8ac83d20c993ff2b7201977db1b 100644 (file)
@@ -27,6 +27,8 @@
 //
 
 using System.Collections.Generic;
+using System.Text;
+using System.Globalization;
 
 namespace System.Net.Http.Headers
 {
@@ -160,7 +162,242 @@ namespace System.Net.Http.Headers
 
                public static bool TryParse (string input, out CacheControlHeaderValue parsedValue)
                {
-                       throw new NotImplementedException ();
+                       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 ();
                }
        }
 }
index e3363e533b326e93ad38e32a60fef9efcc2d938e..1ce4a2a5b57b8bf4ca8ed634162e27b9d9548de7 100644 (file)
@@ -60,5 +60,21 @@ namespace System.Net.Http.Headers
 
                        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]);
+                       }
+               }
        }
 }
index c2e1c2804e9c0a94fe13017e6049809ffc483bcf..80c08ccf6fb6b61f97d9bf78b55985fe10bdeeac 100644 (file)
@@ -26,6 +26,8 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+using System.Globalization;
+
 namespace System.Net.Http.Headers
 {
        struct Token
@@ -39,6 +41,9 @@ namespace System.Net.Http.Headers
                        SeparatorEqual,
                        SeparatorSemicolon,
                        SeparatorSlash,
+                       SeparatorDash,
+                       SeparatorComma,
+                       OpenParens,
                }
 
                readonly Type type;
@@ -100,6 +105,50 @@ namespace System.Net.Http.Headers
                        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 TryGetNumericValue (Token token, out int value)
+               {
+                       return int.TryParse (GetStringValue (token), 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);
+
+                       const DateTimeStyles DefaultStyles = DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowWhiteSpaces;
+
+                   return
+                               DateTimeOffset.TryParseExact (text, "r", DateTimeFormatInfo.InvariantInfo, DefaultStyles, out value) ||
+                               DateTimeOffset.TryParseExact (text, "dddd, dd'-'MMM'-'yy HH:mm:ss 'GMT'", DateTimeFormatInfo.InvariantInfo, DefaultStyles, out value) ||
+                               DateTimeOffset.TryParseExact (text, "ddd MMM d HH:mm:ss yyyy", DateTimeFormatInfo.InvariantInfo, DefaultStyles, out value);
+               }
+
                public static bool IsValidToken (string input)
                {
                        int i = 0;
@@ -115,6 +164,44 @@ namespace System.Net.Http.Headers
                        return i > 0;
                }
 
+               public void EatChar ()
+               {
+                       ++pos;
+               }
+
+               public int PeekChar ()
+               {
+                       return pos < s.Length ? s[pos] : -1;
+               }
+
+               public bool ScanCommentOptional (out string value)
+               {
+                       var t = Scan ();
+                       if (t != Token.Type.OpenParens) {
+                               value = null;
+                               return t == Token.Type.End;
+                       }
+
+                       while (pos < s.Length) {
+                               var ch = s[pos];
+                               if (ch == ')') {
+                                       ++pos;
+                                       var start = t.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;
@@ -146,6 +233,12 @@ namespace System.Net.Http.Headers
                                case '/':
                                        ttype = Token.Type.SeparatorSlash;
                                        break;
+                               case '-':
+                                       ttype = Token.Type.SeparatorDash;
+                                       break;
+                               case ',':
+                                       ttype = Token.Type.SeparatorComma;
+                                       break;
                                case '"':
                                        // Quoted string
                                        start = pos - 1;
@@ -166,6 +259,8 @@ namespace System.Net.Http.Headers
 
                                        break;
                                case '(':
+                                       start = pos - 1;
+                                       ttype = Token.Type.OpenParens;
                                        break;
                                default:
                                        if (ch <= last_token_char && token_chars[ch]) {
index b7e0632f4d640119a77e2b807415547408b510dd..03bcab48c9dc5b3c0063e219bcbfbac410a96444 100644 (file)
@@ -77,6 +77,14 @@ namespace System.Net.Http.Headers
                        }
                }
 
+               internal static NameValueHeaderValue Create (string name, string value)
+               {
+                       return new NameValueHeaderValue () {
+                               Name = name,
+                               value = value
+                       };
+               }
+
                object ICloneable.Clone ()
                {
                        return new NameValueHeaderValue (this);
index 513eccc90f5f7ef6e6aea9d951bf13478ed4ec8d..3ab8eab7bf55ebd851ca6109bbdfca2116b084ec 100644 (file)
@@ -51,6 +51,37 @@ namespace System.Net.Http.Headers
                                        throw new FormatException (s);
                                }
                        }
+
+                       public static void CheckQuotedString (string s)
+                       {
+                               if (s == null)
+                                       throw new ArgumentNullException ();
+
+                               var lexer = new Lexer (s);
+                               if (lexer.Scan () == Headers.Token.Type.QuotedString && lexer.Scan () == Headers.Token.Type.End)
+                                       return;
+
+                               if (s.Length == 0)
+                                       throw new ArgumentException ();
+
+                               throw new FormatException (s);
+                       }
+
+                       public static void CheckComment (string s)
+                       {
+                               if (s == null)
+                                       throw new ArgumentNullException ();
+
+                               var lexer = new Lexer (s);
+
+                               string temp;
+                               if (!lexer.ScanCommentOptional (out temp)) {
+                                       if (s.Length == 0)
+                                               throw new ArgumentException ();
+
+                                       throw new FormatException (s);
+                               }
+                       }
                }
 
                public static class DateTime
@@ -96,7 +127,21 @@ namespace System.Net.Http.Headers
                {
                        public static bool TryParse (string input, out System.Uri result)
                        {
-                               throw new NotImplementedException ();
+                               return System.Uri.TryCreate ("http://" + input + "/", UriKind.Absolute, out result);
+                       }
+
+                       public static void Check (string s)
+                       {
+                               if (s == null)
+                                       throw new ArgumentNullException ();
+
+                               System.Uri uri;
+                               if (!TryParse (s, out uri)) {
+                                       if (s.Length == 0)
+                                               throw new ArgumentException ();
+
+                                       throw new FormatException (s);
+                               }
                        }
                }
        }
index 11fc32cfd6fcf55975a695ef394534aef330cbbd..d95ec3ab4125a768a9cf2effcab3d6e80b5a70f4 100644 (file)
@@ -31,16 +31,24 @@ namespace System.Net.Http.Headers
        public class ProductHeaderValue : ICloneable
        {
                public ProductHeaderValue (string name)
-                       : this (name, null)
                {
+                       Parser.Token.Check (name);
+                       Name = name;
                }
 
                public ProductHeaderValue (string name, string version)
+                       : this (name)
                {
-                       Name = name;
+                       if (version != null)
+                               Parser.Token.Check (version);
+
                        Version = version;
                }
 
+               private ProductHeaderValue ()
+               {
+               }
+
                public string Name { get; private set; }
                public string Version { get; private set; }
 
@@ -79,7 +87,36 @@ namespace System.Net.Http.Headers
 
                public static bool TryParse (string input, out ProductHeaderValue parsedValue)
                {
-                       throw new NotImplementedException ();
+                       parsedValue = null;
+
+                       var lexer = new Lexer (input);
+                       var t = lexer.Scan ();
+                       if (t != Token.Type.Token)
+                               return false;
+
+                       var value = new ProductHeaderValue ();
+                       value.Name = lexer.GetStringValue (t);
+
+                       t = lexer.Scan ();
+                       if (t == Token.Type.SeparatorSlash) {
+                               t = lexer.Scan ();
+                               if (t != Token.Type.Token)
+                                       return false;
+
+                               value.Version = lexer.GetStringValue (t);
+                               t = lexer.Scan ();
+                       }
+
+                       if (t != Token.Type.End)
+                               return false;
+
+                       parsedValue = value;
+                       return true;
+               }
+
+               public override string ToString ()
+               {
+                       return Version == null ? Name : Name + "/" + Version;
                }
        }
 }
index 03bef5e3b759b9fe6e84cbc56e559b41440b039a..0c4d12802a9ad8af0c784220ceb84e65ec01ac24 100644 (file)
@@ -32,11 +32,15 @@ namespace System.Net.Http.Headers
        {
                public ProductInfoHeaderValue (ProductHeaderValue product)
                {
+                       if (product == null)
+                               throw new ArgumentNullException ();
+
                        Product = product;
                }
 
                public ProductInfoHeaderValue (string comment)
                {
+                       Parser.Token.CheckComment (comment);
                        Comment = comment;
                }
 
@@ -45,6 +49,10 @@ namespace System.Net.Http.Headers
                        Product = new ProductHeaderValue (productName, productVersion);
                }
 
+               private ProductInfoHeaderValue ()
+               {
+               }
+
                public string Comment { get; private set; }
                public ProductHeaderValue Product { get; private set; }
 
@@ -82,7 +90,34 @@ namespace System.Net.Http.Headers
                
                public static bool TryParse (string input, out ProductInfoHeaderValue parsedValue)
                {
-                       throw new NotImplementedException ();
+                       parsedValue = null;
+
+                       var lexer = new Lexer (input);
+                       string comment;
+
+                       if (lexer.ScanCommentOptional (out comment)) {
+                               if (comment == null)
+                                       return false;
+
+                               parsedValue = new ProductInfoHeaderValue ();
+                               parsedValue.Comment = comment;
+                               return true;
+                       }
+
+                       ProductHeaderValue res;
+                       if (!ProductHeaderValue.TryParse (input, out res))
+                               return false;
+
+                       parsedValue = new ProductInfoHeaderValue (res);
+                       return true;
+               }
+
+               public override string ToString ()
+               {
+                       if (Product == null)
+                               return Comment;
+
+                       return Product.ToString ();
                }
        }
 }
index e32af2d2aa5bf06665cbd82a97445d25ddbb672e..76ab8f62fa4ee137c839efd73ad30ecc95dce440 100644 (file)
@@ -27,6 +27,7 @@
 //
 
 using System.Collections.Generic;
+using System.Text;
 
 namespace System.Net.Http.Headers
 {
@@ -106,7 +107,111 @@ namespace System.Net.Http.Headers
 
                public static bool TryParse (string input, out RangeHeaderValue parsedValue)
                {
-                       throw new NotImplementedException ();
+                       parsedValue = null;
+
+                       var lexer = new Lexer (input);
+                       var t = lexer.Scan ();
+                       if (t != Token.Type.Token)
+                               return false;
+
+                       var value = new RangeHeaderValue ();
+                       value.unit = lexer.GetStringValue (t);
+
+                       t = lexer.Scan ();
+                       if (t != Token.Type.SeparatorEqual)
+                               return false;
+
+                       bool token_read;
+                       do {
+                               int? from = null, to = null;
+                               int number;
+                               token_read = false;
+
+                               t = lexer.Scan ();
+                               switch (t.Kind) {
+                               case Token.Type.SeparatorDash:
+                                       t = lexer.Scan ();
+                                       if (!lexer.TryGetNumericValue (t, out number))
+                                               return false;
+
+                                       to = number;
+                                       break;
+                               case Token.Type.Token:
+                                       string s = lexer.GetStringValue (t);
+                                       var values = s.Split (new [] { '-' }, StringSplitOptions.RemoveEmptyEntries);
+                                       if (!int.TryParse (values[0], out number))
+                                               return false;
+
+                                       switch (values.Length) {
+                                       case 1:
+                                               t = lexer.Scan ();
+                                               switch (t.Kind) {
+                                               case Token.Type.SeparatorDash:
+                                                       from = number;
+
+                                                       t = lexer.Scan ();
+                                                       if (t != Token.Type.Token) {
+                                                               token_read = true;
+                                                               break;
+                                                       }
+
+                                                       if (!lexer.TryGetNumericValue (t, out number))
+                                                               return false;
+
+                                                       to = number;
+                                                       if (to < from)
+                                                               return false;
+
+                                                       break;
+                                               default:
+                                                       return false;
+                                               }
+                                               break;
+                                       case 2:
+                                               from = number;
+
+                                               if (!int.TryParse (values[1], out number))
+                                                       return false;
+
+                                               to = number;
+                                               if (to < from)
+                                                       return false;
+
+                                               break;
+                                       default:
+                                               return false;
+                                       }
+
+                                       break;
+                               default:
+                                       return false;
+                               }
+
+                               value.Ranges.Add (new RangeItemHeaderValue (from, to));
+                               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 ()
+               {
+                       var sb = new StringBuilder (unit);
+                       sb.Append ("=");
+                       for (int i = 0; i < Ranges.Count; ++i) {
+                               if (i > 0)
+                                       sb.Append (", ");
+
+                               sb.Append (ranges[i]);
+                       }
+
+                       return sb.ToString ();
                }
        }
 }
index 252b518128d52efabee932d91e03a6da6f2d9aac..405c976e8476f7e45fa2abd5fcd5ffc6f9b46c94 100644 (file)
@@ -32,6 +32,19 @@ namespace System.Net.Http.Headers
        {
                public RangeItemHeaderValue (long? from, long? to)
                {
+                       if (from == null && to == null)
+                               throw new ArgumentException ();
+
+                       if (from != null && to != null && from > to) {
+                               throw new ArgumentOutOfRangeException ("from");
+                       }
+
+                       if (from < 0)
+                               throw new ArgumentOutOfRangeException ("from");
+
+                       if (to < 0)
+                               throw new ArgumentOutOfRangeException ("to");
+
                        From = from;
                        To = to;
                }
@@ -54,5 +67,16 @@ namespace System.Net.Http.Headers
                {
                        return From.GetHashCode () ^ To.GetHashCode ();
                }
+
+               public override string ToString ()
+               {
+                       if (From == null)
+                               return "-" + To.Value;
+
+                       if (To == null)
+                               return From.Value + "-";
+
+                       return From.Value + "-" + To.Value;
+               }
        }
 }
index d3877daca07e15b6c441cab7443d097eaf23b962..4da14004e13189d47bd2bf89c2a927d35167c7d2 100644 (file)
@@ -32,6 +32,9 @@ namespace System.Net.Http.Headers
        {
                public ViaHeaderValue (string protocolVersion, string receivedBy)
                {
+                       Parser.Token.Check (protocolVersion);
+                       Parser.Uri.Check (receivedBy);
+
                        ProtocolVersion = protocolVersion;
                        ReceivedBy = receivedBy;
                }
@@ -39,13 +42,23 @@ namespace System.Net.Http.Headers
                public ViaHeaderValue (string protocolVersion, string receivedBy, string protocolName)
                        : this (protocolVersion, receivedBy)
                {
-                       ProtocolName = protocolName;
+                       if (!string.IsNullOrEmpty (protocolName)) {
+                               Parser.Token.Check (protocolName);
+                               ProtocolName = protocolName;
+                       }
                }
 
                public ViaHeaderValue (string protocolVersion, string receivedBy, string protocolName, string comment)
                        : this (protocolVersion, receivedBy, protocolName)
                {
-                       Comment = comment;
+                       if (!string.IsNullOrEmpty (comment)) {
+                               Parser.Token.CheckComment (comment);
+                               Comment = comment;
+                       }
+               }
+
+               private ViaHeaderValue ()
+               {
                }
 
                public string Comment { get; private set; }
@@ -97,7 +110,61 @@ namespace System.Net.Http.Headers
                
                public static bool TryParse (string input, out ViaHeaderValue parsedValue)
                {
-                       throw new NotImplementedException ();
+                       parsedValue = null;
+
+                       var lexer = new Lexer (input);
+
+                       var t = lexer.Scan ();
+                       if (t != Token.Type.Token)
+                               return false;
+
+                       var next = lexer.Scan ();
+                       ViaHeaderValue value = new ViaHeaderValue ();
+
+                       if (next == Token.Type.SeparatorSlash) {
+                               next = lexer.Scan ();
+                               if (next != Token.Type.Token)
+                                       return false;
+
+                               value.ProtocolName = lexer.GetStringValue (t);
+                               value.ProtocolVersion = lexer.GetStringValue (next);
+
+                               next = lexer.Scan ();
+                       } else {
+                               value.ProtocolVersion = lexer.GetStringValue (t);
+                       }
+
+                       if (next != Token.Type.Token)
+                               return false;
+
+                       if (lexer.PeekChar () == ':') {
+                               lexer.EatChar ();
+
+                               t = lexer.Scan ();
+                               if (t != Token.Type.Token)
+                                       return false;
+                       } else {
+                               t = next;
+                       }
+
+                       value.ReceivedBy = lexer.GetStringValue (next, t);
+
+                       string comment;
+                       if (!lexer.ScanCommentOptional (out comment))
+                               return false;
+
+                       value.Comment = comment;
+                       parsedValue = value;
+                       return true;
+               }
+
+               public override string ToString ()
+               {
+                       string s = ProtocolName != null ?
+                               ProtocolName + "/" + ProtocolVersion + " " + ReceivedBy :
+                               ProtocolVersion + " " + ReceivedBy;
+
+                       return Comment != null ? s + " " + Comment : s;
                }
        }
 }
index 442881564e0daa4170962db1e97f640ba3aae891..fc58299cf0bd0d25dff4223992a0a7adb45faa06 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+using System.Globalization;
+
 namespace System.Net.Http.Headers
 {
        public class WarningHeaderValue : ICloneable
        {
                public WarningHeaderValue (int code, string agent, string text)
                {
+                       if (!IsCodeValid (code))
+                               throw new ArgumentOutOfRangeException ("code");
+
+                       Parser.Uri.Check (agent);
+                       Parser.Token.CheckQuotedString (text);
+
                        Code = code;
                        Agent = agent;
                        Text = text;
@@ -43,11 +51,20 @@ namespace System.Net.Http.Headers
                        Date = date;
                }
 
+               private WarningHeaderValue ()
+               {
+               }
+
                public string Agent { get; private set; }
                public int Code { get; private set; }
                public DateTimeOffset? Date { get; private set; }
                public string Text { get; private set; }
 
+               static bool IsCodeValid (int code)
+               {
+                       return code >= 0 && code < 1000;
+               }
+
                object ICloneable.Clone ()
                {
                        return MemberwiseClone ();
@@ -86,7 +103,65 @@ namespace System.Net.Http.Headers
                
                public static bool TryParse (string input, out WarningHeaderValue parsedValue)
                {
-                       throw new NotImplementedException ();
+                       parsedValue = null;
+
+                       var lexer = new Lexer (input);
+                       var t = lexer.Scan ();
+
+                       if (t != Token.Type.Token)
+                               return false;
+
+                       int code;
+                       if (!lexer.TryGetNumericValue (t, out code) || !IsCodeValid (code))
+                               return false;
+
+                       t = lexer.Scan ();
+                       if (t != Token.Type.Token)
+                               return false;
+
+                       var next = t;
+                       if (lexer.PeekChar () == ':') {
+                               lexer.EatChar ();
+
+                               next = lexer.Scan ();
+                               if (next != Token.Type.Token)
+                                       return false;
+                       }
+
+                       var value = new WarningHeaderValue ();
+                       value.Code = code;
+                       value.Agent = lexer.GetStringValue (t, next);
+
+                       t = lexer.Scan ();
+                       if (t != Token.Type.QuotedString)
+                               return false;
+
+                       value.Text = lexer.GetStringValue (t);
+
+                       t = lexer.Scan ();
+                       if (t == Token.Type.QuotedString) {
+                               DateTimeOffset date;
+                               if (!lexer.TryGetDateValue (t, out date))
+                                       return false;
+
+                               value.Date = date;
+                               t = lexer.Scan ();
+                       }
+
+                       if (t != Token.Type.End)
+                               return false;
+
+                       parsedValue = value;
+                       return true;
+               }
+
+               public override string ToString ()
+               {
+                       string s = Code.ToString ("000") + " " + Agent + " " + Text;
+                       if (Date.HasValue)
+                               s = s + " \"" + Date.Value.ToString ("r", CultureInfo.InvariantCulture) + "\"";
+
+                       return s;
                }
        }
 }
index aaf280165d801879a164e1cc6fcc1d00073cb82c..fe6bb6f3426b214cc8c760ad57cabf1d96b5df60 100644 (file)
@@ -73,6 +73,13 @@ namespace MonoTests.System.Net.Http.Headers
                        var res = AuthenticationHeaderValue.Parse ("c");
                        Assert.AreEqual ("c", res.Scheme, "#1");
                        Assert.IsNull (res.Parameter, "#2");
+                       Assert.AreEqual ("c", res.ToString (), "#3");
+
+                       res = AuthenticationHeaderValue.Parse ("ss   p=3 , q = \"vvv\"");
+
+                       Assert.AreEqual ("ss", res.Scheme, "#11");
+                       Assert.AreEqual ("p=3 , q = \"vvv\"", res.Parameter, "#12");
+                       Assert.AreEqual ("ss p=3 , q = \"vvv\"", res.ToString (), "#13");
                }
 
                [Test]
index ca05e342e33ac66b63e530109ea384f30cb934c2..d413147516f95a235039887375621a9dc7129847 100644 (file)
@@ -74,9 +74,44 @@ namespace MonoTests.System.Net.Http.Headers
                        var res = CacheControlHeaderValue.Parse ("audio");
                        Assert.AreEqual ("audio", res.Extensions.First ().Name, "#1");
                        Assert.IsNull (res.Extensions.First ().Value, "#2");
+                       Assert.AreEqual ("audio", res.ToString (), "#3");
 
                        res = CacheControlHeaderValue.Parse (null);
-                       Assert.IsNull (res, "#3");
+                       Assert.IsNull (res, "#4");
+
+                       res = CacheControlHeaderValue.Parse ("no-cache, no-store , max-age = 44, max-stale = 66, min-fresh=333 ,no-transform, only-if-cached");
+                       Assert.IsTrue (res.NoCache, "#10");
+                       Assert.IsTrue (res.NoStore, "#11");
+                       Assert.AreEqual (new TimeSpan (0, 0, 44), res.MaxAge, "#12");
+                       Assert.IsTrue (res.MaxStale, "#13");
+                       Assert.AreEqual (new TimeSpan (0, 1, 6), res.MaxStaleLimit, "#14");
+                       Assert.AreEqual (new TimeSpan (0, 5, 33), res.MinFresh, "#15");
+                       Assert.IsTrue (res.NoTransform, "#16");
+                       Assert.IsTrue (res.OnlyIfCached, "#17");
+                       Assert.AreEqual ("no-store, no-transform, only-if-cached, no-cache, max-age=44, max-stale=66, min-fresh=333", res.ToString (), "#18");
+
+                       res = CacheControlHeaderValue.Parse ("anu = 333");
+                       Assert.AreEqual ("anu", res.Extensions.First ().Name, "#20");
+                       Assert.AreEqual ("333", res.Extensions.First ().Value, "#21");
+                       Assert.AreEqual ("anu=333", res.ToString (), "#22");
+
+                       res = CacheControlHeaderValue.Parse ("public, private = \"nnn \", no-cache =\"mmm\" , must-revalidate, proxy-revalidate, s-maxage=443");
+                       Assert.IsTrue (res.Public, "#30");
+                       Assert.IsTrue (res.Private, "#31");
+                       Assert.AreEqual ("nnn", res.PrivateHeaders.First (), "#32");
+                       Assert.IsTrue (res.NoCache, "#33");
+                       Assert.AreEqual ("mmm", res.NoCacheHeaders.First (), "#34");
+                       Assert.IsTrue (res.MustRevalidate, "#35");
+                       Assert.IsTrue (res.ProxyRevalidate, "#36");
+                       Assert.AreEqual (new TimeSpan (0, 7, 23), res.SharedMaxAge, "#37");
+                       Assert.AreEqual ("public, must-revalidate, proxy-revalidate, no-cache=\"mmm\", s-maxage=443, private=\"nnn\"", res.ToString (), "#38");
+
+                       res = CacheControlHeaderValue.Parse ("private = \"nnn , oo, xx\", bb= \" x \"");
+                       Assert.IsTrue (res.Private, "#40");
+                       Assert.AreEqual (3, res.PrivateHeaders.Count, "#41");
+                       Assert.AreEqual ("oo", res.PrivateHeaders.ToList ()[1], "#42");
+                       Assert.AreEqual ("\" x \"", res.Extensions.First ().Value, "#43");
+                       Assert.AreEqual ("private=\"nnn, oo, xx\", bb=\" x \"", res.ToString (), "#44");
                }
 
                [Test]
@@ -87,6 +122,18 @@ namespace MonoTests.System.Net.Http.Headers
                                Assert.Fail ("#1");
                        } catch (FormatException) {
                        }
+
+                       try {
+                               CacheControlHeaderValue.Parse ("max-age=");
+                               Assert.Fail ("#2");
+                       } catch (FormatException) {
+                       }
+
+                       try {
+                               CacheControlHeaderValue.Parse ("me=");
+                               Assert.Fail ("#3");
+                       } catch (FormatException) {
+                       }
                }
 
                [Test]
index e8341dfdaeb7f5bdf4647edf558e419d949ad8ce..9ae06c1094bcfc6a5b4b25be1e89a7c2cd8482f1 100644 (file)
@@ -51,6 +51,18 @@ namespace MonoTests.System.Net.Http.Headers
                                Assert.Fail ("#2");
                        } catch (FormatException) {
                        }
+
+                       try {
+                               new ProductHeaderValue ("/");
+                               Assert.Fail ("#3");
+                       } catch (FormatException) {
+                       }
+               }
+
+               [Test]
+               public void Ctor ()
+               {
+                       new ProductHeaderValue ("aa", null);
                }
 
                [Test]
@@ -73,6 +85,12 @@ namespace MonoTests.System.Net.Http.Headers
                        var res = ProductHeaderValue.Parse ("c");
                        Assert.AreEqual ("c", res.Name, "#1");
                        Assert.IsNull (res.Version, "#2");
+                       Assert.AreEqual ("c", res.ToString (), "#3");
+
+                       res = ProductHeaderValue.Parse (" mm / ppp");
+                       Assert.AreEqual ("mm", res.Name, "#4");
+                       Assert.AreEqual ("ppp", res.Version, "#5");
+                       Assert.AreEqual ("mm/ppp", res.ToString (), "#6");
                }
 
                [Test]
@@ -95,6 +113,12 @@ namespace MonoTests.System.Net.Http.Headers
                                Assert.Fail ("#3");
                        } catch (FormatException) {
                        }
+
+                       try {
+                               ProductHeaderValue.Parse ("a/");
+                               Assert.Fail ("#4");
+                       } catch (FormatException) {
+                       }
                }
 
                [Test]
index 80591f2df17da0036ac5d3bd4084cf1186e3e86a..3f4564f65b8f1eccf8a95280a91ed908b831dd26 100644 (file)
@@ -80,6 +80,18 @@ namespace MonoTests.System.Net.Http.Headers
                        Assert.AreEqual ("c", res.Product.Name, "#1");
                        Assert.IsNull (res.Product.Version, "#2");
                        Assert.IsNull (res.Comment, "#3");
+                       Assert.AreEqual ("c", res.ToString (), "#4");
+
+                       res = ProductInfoHeaderValue.Parse (" b / 6");
+                       Assert.AreEqual ("b", res.Product.Name, "#11");
+                       Assert.AreEqual ("6", res.Product.Version, "#12");
+                       Assert.IsNull (res.Comment, "#13");
+                       Assert.AreEqual ("b/6", res.ToString (), "#14");
+
+                       res = ProductInfoHeaderValue.Parse (" (  cccc )   ");
+                       Assert.IsNull (res.Product, "#21");
+                       Assert.AreEqual ("(  cccc )", res.Comment, "#22");
+                       Assert.AreEqual ("(  cccc )", res.ToString (), "#23");
                }
 
                [Test]
index 72743b41211fe26286a5886e94cf141352a254e0..17be0561fa63c0a7a9debf679a12f1b3ad012ab9 100644 (file)
@@ -81,6 +81,27 @@ namespace MonoTests.System.Net.Http.Headers
                        Assert.AreEqual ("bytes", res.Unit, "#1");
                        Assert.AreEqual (2, res.Ranges.First ().From, "#2");
                        Assert.AreEqual (40, res.Ranges.First ().To, "#3");
+                       Assert.AreEqual ("bytes=2-40", res.ToString (), "#4");
+
+                       res = RangeHeaderValue.Parse ("d-dd = 2 - ");
+                       Assert.AreEqual ("d-dd", res.Unit, "#10");
+                       Assert.AreEqual (2, res.Ranges.First ().From, "#11");
+                       Assert.IsNull (res.Ranges.First ().To, "#12");
+                       Assert.AreEqual ("d-dd=2-", res.ToString (), "#13");
+
+                       res = RangeHeaderValue.Parse ("zz = - 6 , 5 - 9, -8");
+                       Assert.AreEqual ("zz", res.Unit, "#20");
+                       Assert.IsNull (res.Ranges.First ().From, "#21");
+                       Assert.AreEqual (6, res.Ranges.First ().To, "#22");
+                       Assert.AreEqual (5, res.Ranges.Skip (1).First ().From, "#21b");
+                       Assert.AreEqual (9, res.Ranges.Skip (1).First ().To, "#22b");
+                       Assert.AreEqual ("zz=-6, 5-9, -8", res.ToString (), "#23");
+
+                       res = RangeHeaderValue.Parse ("ddd = 2 -, 1-4");
+                       Assert.AreEqual ("ddd", res.Unit, "#30");
+                       Assert.AreEqual (2, res.Ranges.First ().From, "#31");
+                       Assert.IsNull (res.Ranges.First ().To, "#32");
+                       Assert.AreEqual ("ddd=2-, 1-4", res.ToString (), "#33");
                }
 
                [Test]
@@ -109,6 +130,18 @@ namespace MonoTests.System.Net.Http.Headers
                                Assert.Fail ("#4");
                        } catch (FormatException) {
                        }
+
+                       try {
+                               RangeHeaderValue.Parse ("byte=1");
+                               Assert.Fail ("#5");
+                       } catch (FormatException) {
+                       }
+
+                       try {
+                               RangeHeaderValue.Parse ("byte=10-6");
+                               Assert.Fail ("#6");
+                       } catch (FormatException) {
+                       }
                }
 
                [Test]
index c58694965a89cd39579db6a97abd846dcdfd1334..f6abc397bbe12bf4512e6390dec77cd319b2e6eb 100644 (file)
@@ -51,12 +51,22 @@ namespace MonoTests.System.Net.Http.Headers
                                Assert.Fail ("#2");
                        } catch (ArgumentOutOfRangeException) {
                        }
+
+                       try {
+                               new RangeItemHeaderValue (-1, 2);
+                               Assert.Fail ("#3");
+                       } catch (ArgumentOutOfRangeException) {
+                       }
                }
 
                [Test]
                public void Ctor ()
                {
-                       new RangeItemHeaderValue (1, null);
+                       var v = new RangeItemHeaderValue (1, null);
+                       Assert.AreEqual ("1-", v.ToString (), "#1");
+
+                       v = new RangeItemHeaderValue (null, 1);
+                       Assert.AreEqual ("-1", v.ToString (), "#2");
                }
 
                [Test]
index 22a6a4d38b9ff6b212aacb693f73700f7d979753..2cc0e20e9b00ae379b74b5e8875a0275f444017e 100644 (file)
@@ -54,7 +54,7 @@ namespace MonoTests.System.Net.Http.Headers
 
                        try {
                                new StringWithQualityHeaderValue ("s", 1.1);
-                               Assert.Fail ("#2");
+                               Assert.Fail ("#3");
                        } catch (ArgumentOutOfRangeException) {
                        }
                }
@@ -66,6 +66,7 @@ namespace MonoTests.System.Net.Http.Headers
                        Assert.AreEqual (value, new StringWithQualityHeaderValue ("ab"), "#1");
                        Assert.AreEqual (value, new StringWithQualityHeaderValue ("AB"), "#2");
                        Assert.AreNotEqual (value, new StringWithQualityHeaderValue ("AA"), "#3");
+                       Assert.AreEqual ("ab", value.ToString (), "#33");
 
                        value = new StringWithQualityHeaderValue ("ab", 1);
                        Assert.AreEqual (value, new StringWithQualityHeaderValue ("Ab", 1), "#4");
index 58df167d3b0acd07a5ff896098bac636b73655bf..c8a30223259779ed13843fab26dde05efb6c6cec 100644 (file)
@@ -54,9 +54,21 @@ namespace MonoTests.System.Net.Http.Headers
 
                        try {
                                new ViaHeaderValue ("a", null);
-                               Assert.Fail ("#1");
+                               Assert.Fail ("#3");
                        } catch (ArgumentException) {
                        }
+
+                       try {
+                               new ViaHeaderValue ("a", "b", "::");
+                               Assert.Fail ("#4");
+                       } catch (FormatException) {
+                       }
+
+                       try {
+                               new ViaHeaderValue ("a", "b", null, "(aaa");
+                               Assert.Fail ("#5");
+                       } catch (FormatException) {
+                       }
                }
 
                [Test]
@@ -87,6 +99,14 @@ namespace MonoTests.System.Net.Http.Headers
                        Assert.IsNull (res.ProtocolName, "#1");
                        Assert.AreEqual ("nowhere.com", res.ReceivedBy, "#2");
                        Assert.AreEqual ("1.1", res.ProtocolVersion, "#3");
+                       Assert.AreEqual ("1.1 nowhere.com", res.ToString (), "#4");
+
+                       res = ViaHeaderValue.Parse ("foo / 1.1 nowhere.com:43   ( lalala ) ");
+                       Assert.AreEqual ("foo", res.ProtocolName, "#10");
+                       Assert.AreEqual ("1.1", res.ProtocolVersion, "#11");
+                       Assert.AreEqual ("nowhere.com:43", res.ReceivedBy, "#12");
+                       Assert.AreEqual ("( lalala )", res.Comment, "#13");
+                       Assert.AreEqual ("foo/1.1 nowhere.com:43 ( lalala )", res.ToString (), "#14");
                }
 
                [Test]
@@ -105,10 +125,16 @@ namespace MonoTests.System.Net.Http.Headers
                        }
 
                        try {
-                               ViaHeaderValue.Parse ("a;b");
+                               ViaHeaderValue.Parse ("a");
                                Assert.Fail ("#3");
                        } catch (FormatException) {
                        }
+
+                       try {
+                               ViaHeaderValue.Parse ("1 nowhere.com :43");
+                               Assert.Fail ("#4");
+                       } catch (FormatException) {
+                       }
                }
 
                [Test]
index 9d48906b92fa62aa3c3a22e3e5af99c6ba151d41..0b1d1d35ef3fc015e9c1ffa00fb0b3933ff88f05 100644 (file)
@@ -81,6 +81,14 @@ namespace MonoTests.System.Net.Http.Headers
                        Assert.AreEqual (1, res.Code, "#2");
                        Assert.AreEqual ("n", res.Agent, "#3");
                        Assert.AreEqual ("\"\"", res.Text, "#4");
+                       Assert.AreEqual ("001 n \"\"", res.ToString (), "#5");
+
+                       res = WarningHeaderValue.Parse ("155 foo:8080 \"tttext \" \"Sun, 06 Nov 1994 08:49:37 GMT\" ");
+                       Assert.AreEqual (res.Date, new DateTimeOffset (1994, 11, 6, 8, 49, 37, TimeSpan.Zero), "#11");
+                       Assert.AreEqual (155, res.Code, "#12");
+                       Assert.AreEqual ("foo:8080", res.Agent, "#13");
+                       Assert.AreEqual ("\"tttext \"", res.Text, "#14");
+                       Assert.AreEqual ("155 foo:8080 \"tttext \" \"Sun, 06 Nov 1994 08:49:37 GMT\"", res.ToString (), "#5");
                }
 
                [Test]
@@ -103,6 +111,12 @@ namespace MonoTests.System.Net.Http.Headers
                                Assert.Fail ("#3");
                        } catch (FormatException) {
                        }
+
+                       try {
+                               WarningHeaderValue.Parse ("5555 foo:8080 \"\"");
+                               Assert.Fail ("#4");
+                       } catch (FormatException) {
+                       }
                }