1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. 2 3 using System.Collections.Generic; 4 using System.Collections.ObjectModel; 5 using System.Globalization; 6 using System.Linq; 7 using System.Net.Http.Formatting; 8 using System.Net.Http.Headers; 9 using System.Text; 10 using System.Xml; 11 using Newtonsoft.Json.Linq; 12 13 namespace System.Net.Http 14 { 15 /// <summary> 16 /// Provides various internal utility functions 17 /// </summary> 18 internal static class FormattingUtilities 19 { 20 // Supported date formats for input. 21 private static readonly string[] dateFormats = new string[] 22 { 23 // "r", // RFC 1123, required output format but too strict for input 24 "ddd, d MMM yyyy H:m:s 'GMT'", // RFC 1123 (r, except it allows both 1 and 01 for date and time) 25 "ddd, d MMM yyyy H:m:s", // RFC 1123, no zone - assume GMT 26 "d MMM yyyy H:m:s 'GMT'", // RFC 1123, no day-of-week 27 "d MMM yyyy H:m:s", // RFC 1123, no day-of-week, no zone 28 "ddd, d MMM yy H:m:s 'GMT'", // RFC 1123, short year 29 "ddd, d MMM yy H:m:s", // RFC 1123, short year, no zone 30 "d MMM yy H:m:s 'GMT'", // RFC 1123, no day-of-week, short year 31 "d MMM yy H:m:s", // RFC 1123, no day-of-week, short year, no zone 32 33 "dddd, d'-'MMM'-'yy H:m:s 'GMT'", // RFC 850 34 "dddd, d'-'MMM'-'yy H:m:s", // RFC 850 no zone 35 "ddd MMM d H:m:s yyyy", // ANSI C's asctime() format 36 37 "ddd, d MMM yyyy H:m:s zzz", // RFC 5322 38 "ddd, d MMM yyyy H:m:s", // RFC 5322 no zone 39 "d MMM yyyy H:m:s zzz", // RFC 5322 no day-of-week 40 "d MMM yyyy H:m:s", // RFC 5322 no day-of-week, no zone 41 }; 42 43 // Valid header token characters are within the range 0x20 < c < 0x7F excluding the following characters 44 private const string NonTokenChars = "()<>@,;:\\\"/[]?={}"; 45 46 /// <summary> 47 /// The default max depth for our formatter is 256 48 /// </summary> 49 public const int DefaultMaxDepth = 256; 50 51 /// <summary> 52 /// The default min depth for our formatter is 1 53 /// </summary> 54 public const int DefaultMinDepth = 1; 55 56 /// <summary> 57 /// HTTP X-Requested-With header field name 58 /// </summary> 59 public const string HttpRequestedWithHeader = @"x-requested-with"; 60 61 /// <summary> 62 /// HTTP X-Requested-With header field value 63 /// </summary> 64 public const string HttpRequestedWithHeaderValue = @"xmlhttprequest"; 65 66 /// <summary> 67 /// HTTP Host header field name 68 /// </summary> 69 public const string HttpHostHeader = "Host"; 70 71 /// <summary> 72 /// HTTP Version token 73 /// </summary> 74 public const string HttpVersionToken = "HTTP"; 75 76 /// <summary> 77 /// A <see cref="Type"/> representing <see cref="UTF8Encoding"/>. 78 /// </summary> 79 public static readonly Type Utf8EncodingType = typeof(UTF8Encoding); 80 81 /// <summary> 82 /// A <see cref="Type"/> representing <see cref="UnicodeEncoding"/>. 83 /// </summary> 84 public static readonly Type Utf16EncodingType = typeof(UnicodeEncoding); 85 86 /// <summary> 87 /// A <see cref="Type"/> representing <see cref="HttpRequestMessage"/>. 88 /// </summary> 89 public static readonly Type HttpRequestMessageType = typeof(HttpRequestMessage); 90 91 /// <summary> 92 /// A <see cref="Type"/> representing <see cref="HttpResponseMessage"/>. 93 /// </summary> 94 public static readonly Type HttpResponseMessageType = typeof(HttpResponseMessage); 95 96 /// <summary> 97 /// A <see cref="Type"/> representing <see cref="HttpContent"/>. 98 /// </summary> 99 public static readonly Type HttpContentType = typeof(HttpContent); 100 101 /// <summary> 102 /// A <see cref="Type"/> representing <see cref="DelegatingEnumerable{T}"/>. 103 /// </summary> 104 public static readonly Type DelegatingEnumerableGenericType = typeof(DelegatingEnumerable<>); 105 106 /// <summary> 107 /// A <see cref="Type"/> representing <see cref="IEnumerable{T}"/>. 108 /// </summary> 109 public static readonly Type EnumerableInterfaceGenericType = typeof(IEnumerable<>); 110 111 /// <summary> 112 /// A <see cref="Type"/> representing <see cref="IQueryable{T}"/>. 113 /// </summary> 114 public static readonly Type QueryableInterfaceGenericType = typeof(IQueryable<>); 115 116 /// <summary> 117 /// Determines whether <paramref name="type"/> is a <see cref="JToken"/> type. 118 /// </summary> 119 /// <param name="type">The type to test.</param> 120 /// <returns> 121 /// <c>true</c> if <paramref name="type"/> is a <see cref="JToken"/> type; otherwise, <c>false</c>. 122 /// </returns> IsJTokenType(Type type)123 public static bool IsJTokenType(Type type) 124 { 125 return typeof(JToken).IsAssignableFrom(type); 126 } 127 128 /// <summary> 129 /// Creates an empty <see cref="HttpContentHeaders"/> instance. The only way is to get it from a dummy 130 /// <see cref="HttpContent"/> instance. 131 /// </summary> 132 /// <returns>The created instance.</returns> CreateEmptyContentHeaders()133 public static HttpContentHeaders CreateEmptyContentHeaders() 134 { 135 HttpContent tempContent = null; 136 HttpContentHeaders contentHeaders = null; 137 try 138 { 139 tempContent = new StringContent(String.Empty); 140 contentHeaders = tempContent.Headers; 141 contentHeaders.Clear(); 142 } 143 finally 144 { 145 // We can dispose the content without touching the headers 146 if (tempContent != null) 147 { 148 tempContent.Dispose(); 149 } 150 } 151 152 return contentHeaders; 153 } 154 155 /// <summary> 156 /// Ensure the actual collection is identical to the expected one 157 /// </summary> 158 /// <param name="actual">The actual collection of the instance</param> 159 /// <param name="expected">The expected collection of the instance</param> 160 /// <returns>Returns true if they are identical</returns> ValidateCollection(Collection<MediaTypeHeaderValue> actual, MediaTypeHeaderValue[] expected)161 public static bool ValidateCollection(Collection<MediaTypeHeaderValue> actual, MediaTypeHeaderValue[] expected) 162 { 163 if (actual.Count != expected.Length) 164 { 165 return false; 166 } 167 168 foreach (MediaTypeHeaderValue value in expected) 169 { 170 if (!actual.Contains(value)) 171 { 172 return false; 173 } 174 } 175 176 return true; 177 } 178 179 /// <summary> 180 /// Create a default reader quotas with a default depth quota of 1K 181 /// </summary> 182 /// <returns></returns> CreateDefaultReaderQuotas()183 public static XmlDictionaryReaderQuotas CreateDefaultReaderQuotas() 184 { 185 return new XmlDictionaryReaderQuotas() 186 { 187 MaxArrayLength = Int32.MaxValue, 188 MaxBytesPerRead = Int32.MaxValue, 189 MaxDepth = DefaultMaxDepth, 190 MaxNameTableCharCount = Int32.MaxValue, 191 MaxStringContentLength = Int32.MaxValue 192 }; 193 } 194 195 /// <summary> 196 /// Remove bounding quotes on a token if present 197 /// </summary> 198 /// <param name="token">Token to unquote.</param> 199 /// <returns>Unquoted token.</returns> UnquoteToken(string token)200 public static string UnquoteToken(string token) 201 { 202 if (String.IsNullOrWhiteSpace(token)) 203 { 204 return token; 205 } 206 207 if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1) 208 { 209 return token.Substring(1, token.Length - 2); 210 } 211 212 return token; 213 } 214 ValidateHeaderToken(string token)215 public static bool ValidateHeaderToken(string token) 216 { 217 return token != null && !token.Any(c => c < 0x21 || c > 0x7E || NonTokenChars.IndexOf(c) != -1); 218 } 219 DateToString(DateTimeOffset dateTime)220 public static string DateToString(DateTimeOffset dateTime) 221 { 222 // Format according to RFC1123; 'r' uses invariant info (DateTimeFormatInfo.InvariantInfo) 223 return dateTime.ToUniversalTime().ToString("r", CultureInfo.InvariantCulture); 224 } 225 TryParseDate(string input, out DateTimeOffset result)226 public static bool TryParseDate(string input, out DateTimeOffset result) 227 { 228 return DateTimeOffset.TryParseExact(input, dateFormats, DateTimeFormatInfo.InvariantInfo, 229 DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal, 230 out result); 231 } 232 233 /// <summary> 234 /// Parses valid integer strings with no leading signs, whitespace or other <see cref="NumberStyles"/> 235 /// </summary> 236 /// <param name="value">The value to parse</param> 237 /// <param name="result">The result</param> 238 /// <returns>True if value was valid; false otherwise.</returns> TryParseInt32(string value, out int result)239 public static bool TryParseInt32(string value, out int result) 240 { 241 return Int32.TryParse(value, NumberStyles.None, NumberFormatInfo.InvariantInfo, out result); 242 } 243 } 244 } 245