5 // Marek Safar <marek.safar@gmail.com>
7 // Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System.Globalization;
31 namespace System.Net.Http.Headers
49 public static readonly Token Empty = new Token (Type.Token, 0, 0);
53 public Token (Type type, int startPosition, int endPosition)
57 StartPosition = startPosition;
58 EndPosition = endPosition;
61 public int StartPosition { get; private set; }
62 public int EndPosition { get; private set; }
70 public static implicit operator Token.Type (Token token)
75 public override string ToString ()
77 return type.ToString ();
83 // any CHAR except CTLs or separators
84 static readonly bool[] token_chars = {
85 /*0*/ false, false, false, false, false, false, false, false, false, false,
86 /*10*/ false, false, false, false, false, false, false, false, false, false,
87 /*20*/ false, false, false, false, false, false, false, false, false, false,
88 /*30*/ false, false, false, true, false, true, true, true, true, true,
89 /*40*/ false, false, true, true, false, true, true, false, true, true,
90 /*50*/ true, true, true, true, true, true, true, true, false, false,
91 /*60*/ false, false, false, false, false, true, true, true, true, true,
92 /*70*/ true, true, true, true, true, true, true, true, true, true,
93 /*80*/ true, true, true, true, true, true, true, true, true, true,
94 /*90*/ true, false, false, false, true, true, true, true, true, true,
95 /*100*/ true, true, true, true, true, true, true, true, true, true,
96 /*110*/ true, true, true, true, true, true, true, true, true, true,
97 /*120*/ true, true, true, false, true, false, true
100 static readonly int last_token_char = token_chars.Length;
101 static readonly string[] dt_formats = new[] {
103 "dddd, dd'-'MMM'-'yy HH:mm:ss 'GMT'",
104 "ddd MMM d HH:mm:ss yyyy",
106 "ddd, d MMM yyyy H:m:s zzz"
112 public Lexer (string stream)
117 public int Position {
126 public string GetStringValue (Token token)
128 return s.Substring (token.StartPosition, token.EndPosition - token.StartPosition);
131 public string GetStringValue (Token start, Token end)
133 return s.Substring (start.StartPosition, end.EndPosition - start.StartPosition);
136 public string GetQuotedStringValue (Token start)
138 return s.Substring (start.StartPosition + 1, start.EndPosition - start.StartPosition - 2);
141 public string GetRemainingStringValue (int position)
143 return position > s.Length ? null : s.Substring (position);
146 public bool IsStarStringValue (Token token)
148 return (token.EndPosition - token.StartPosition) == 1 && s[token.StartPosition] == '*';
151 public bool TryGetNumericValue (Token token, out int value)
153 return int.TryParse (GetStringValue (token), NumberStyles.None, CultureInfo.InvariantCulture, out value);
156 public bool TryGetNumericValue (Token token, out long value)
158 return long.TryParse (GetStringValue (token), NumberStyles.None, CultureInfo.InvariantCulture, out value);
161 public TimeSpan? TryGetTimeSpanValue (Token token)
164 if (TryGetNumericValue (token, out seconds)) {
165 return TimeSpan.FromSeconds (seconds);
171 public bool TryGetDateValue (Token token, out DateTimeOffset value)
173 string text = token == Token.Type.QuotedString ?
174 s.Substring (token.StartPosition + 1, token.EndPosition - token.StartPosition - 2) :
175 GetStringValue (token);
177 return TryGetDateValue (text, out value);
180 public static bool TryGetDateValue (string text, out DateTimeOffset value)
182 const DateTimeStyles DefaultStyles = DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowWhiteSpaces;
184 return DateTimeOffset.TryParseExact (text, dt_formats, DateTimeFormatInfo.InvariantInfo, DefaultStyles, out value);
187 public bool TryGetDoubleValue (Token token, out double value)
189 string s = GetStringValue (token);
190 return double.TryParse (s, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out value);
193 public static bool IsValidToken (string input)
197 // any CHAR except CTLs or separator
199 for (; i < input.Length; ++i) {
201 if (!IsValidCharacter (s))
208 public static bool IsValidCharacter (char input)
210 return input < last_token_char && token_chars[input];
213 public void EatChar ()
218 public int PeekChar ()
220 return pos < s.Length ? s[pos] : -1;
223 public bool ScanCommentOptional (out string value)
226 if (ScanCommentOptional (out value, out t))
229 return t == Token.Type.End;
232 public bool ScanCommentOptional (out string value, out Token readToken)
235 if (readToken != Token.Type.OpenParens) {
241 while (pos < s.Length) {
254 var start = readToken.StartPosition;
255 value = s.Substring (start, pos - start);
259 // any OCTET except CTLs, but including LWS
260 if (ch < 32 || ch > 126)
270 public Token Scan (bool recognizeDash = false)
274 return new Token (Token.Type.Error, 0, 0);
277 if (pos >= s.Length) {
278 ttype = Token.Type.End;
280 ttype = Token.Type.Error;
286 if (pos == s.Length) {
287 ttype = Token.Type.End;
293 ttype = Token.Type.SeparatorEqual;
296 ttype = Token.Type.SeparatorSemicolon;
299 ttype = Token.Type.SeparatorSlash;
303 ttype = Token.Type.SeparatorDash;
309 ttype = Token.Type.SeparatorComma;
314 while (pos < s.Length) {
318 // The backslash character ("\") MAY be used as a single-character
319 // quoting mechanism only within quoted-string
322 if (pos + 1 < s.Length) {
331 ttype = Token.Type.QuotedString;
339 ttype = Token.Type.OpenParens;
342 if (ch < last_token_char && token_chars[ch]) {
345 ttype = Token.Type.Token;
346 while (pos < s.Length) {
348 if (ch >= last_token_char || !token_chars[ch]) {
360 return new Token (ttype, start, pos);